Helping Entity Framework v4.0 play it’s <ROLE> – Part 4.0
Before diving in you might like to read parts 1, 2 and 3 if you have come to this post out of sync. Also if you are not familiar with some of the newer aspects of the Code Only CTP for Entity Framework it might be worth looking here on the ADO.Net Team blog.
The Service Layer
I want to point out that this approach in development is not restricted to a technology or architecture per`se and in discussing how it fits with a ‘Service Layer’ is equally valid for a Layered Request / Response style Architecture and asynchronous messaging systems with very well defined business components.
Let’s start with a run of the mill WCF approach – I have purposely elided the interface contract for the sake of brevity.
[OperationContract(IsOneWay=true)] [OperationBehaviour(TranactionScopeRequired=true, TransactionScopeAutoComplete=false)] public void DiscountRunOutProducts() { IList<IRunOutDiscountForProducts> products = null; var compParams = new Dictionary<string, string>(); compParams.Add(_conParamName, _connectionStringOnly); var repo = FetchSpec .Configure .With(new IoC()) .AndBuildSession<IRunOutDiscountForProducts>(compParams) .ForRepository(); using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = SessionBuilder<IRunOutDiscountForProducts>.ScopeIsolation() })) { products = repo.Get<IRunOutDiscountForProducts>(); foreach (var product in products) { product.DiscountProductForRunOut(); } scope.Complete(); } }
Every expression of intent here is explicit, from the name of the service operation, all the way to the application of the ROLE to the ORM. From an ongoing maintenance perspective, when any of the given actors need to be varied through the life cycle of the software, this can be done independently. For example, we don’t have to revisit our service layers code when we profile the system and find that the fetching strategy should be eager fetching for a given scenario, instead we can build a new version of the existing Fetching Strategy and drop it in. This kind of flexibility ripples through the system, with the same said for Specifications, Message Handlers, so on and so forth. If of course you wanted to change the code to accept a Specification ‘in line’ rather than have it deployed and dynamically resolved, then you could probably have just implemented an overloaded version of the Service operation to begin with and pass in the parameters required to build those pieces at runtime. The example is a one way operation but you could easily be returning values and accepting parameters (think DTO’s) in a request / response scenario. Bear in mind these DTO’s are not being mapped and subsequently attached, they are either parameters for the ORM or return property buckets for binding or mapping to a presentation model.
For a point of difference, let’s take a look at how this might work if our Service Layer was implemented on top of the popular Open Source Framework NServiceBus.
public class DiscountRunOutProductsMessage: IMessage { public string DiscountedRegion{ get; set; } public decimal UnitPrice{ get; set; } } public class DiscountRunOutProducts : IMessageHandler<DiscountRunOutProductsMessage> { public void Handle(DiscountRunOutProductsMessage message) { IList<IRunOutDiscountForProducts> products = null; var compParams = new Dictionary<string, string>(); compParams.Add(_conParamName, _connectionStringOnly); var repo = FetchSpec.Configure .With(new IoC()) .AndBuildSession<IRunOutDiscountForProducts> (compParams) .ForRepository(); using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = SessionBuilder<IRunOutDiscountForProducts>.ScopeIsolation() })) { var spec = new Specification<IRunOutDiscountForProducts> (p => p.UnitPrice < message.UnitPrice && p.Supplier.Region = message.DiscountedRegion); products = repo.Get<IRunOutDiscountForProducts>(spec); foreach (var product in products) { product.DiscountProductForRunOut(); } scope.Complete(); } } }
Something worth pointing out in our NServiceBus example is that it too is quite explicit. The message handler, handles messages expressly for the purpose of discounting products to be ‘run out’, exemplifying how the pattern is becoming more entrenched in our style of working. Once again, if we don’t like anything about the way the handler is doing it’s work, we can throw it away in preference for a new implementation.
Not to be left out
Steve left a comment and asked me not to forgo examples that dealt with inserting and updating data, Steve also wanted to know about how I go about dealing with the Unit of Work and the ‘per session’ vs. ‘conversation’ and the differences that spring up and also when working with WCF and dealing with optimistic concurrency. Fortunately this part in the series takes aim squarely at how to go about using Entity Framework in concert with Roles from the Service Layer and we tackle those questions by necessity.
Listing 1.0 – Inserts
public void add_a_product() { var compParams = new Dictionary<string, string>(); compParams.Add(_conParamName, _connectionStringOnly); var repo = FetchSpec.Configure .With(new StubIoc() { ScannedAssemblyPath = _scannedAssemblyPath }) .AndBuildSession<ICustomerToAddOrder>(compParams) .ForRepository(); var spec = new Specification<ICustomerToAddOrder>(c => c.CustomerID == “ALFKI”); var config = new IQueryConfigurable[] { spec }; using (var scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions() { IsolationLevel = SessionBuilder<ICustomerToAddOrder>.ScopeIsolation() })) { dynamic customer = repo.Get<ICustomerToAddOrder>(config).First(); var order = repo.IocContainer .ConvertFromRoleToImpl<IOrderToAddToCustomer>(); order.CustomerID = customer.CustomerID; order.Customer = customer; customer.AddOrder(order); repo.Save(); scope.Complete(); } }
Not entirely in the spirit of things
Notice the use of the dynamic keyword above (suggested by Mark) Let me explain. Due the Entity Frameworks hard and fast rule of only allowing EntityConfiguration<T> to specify a concrete class as it’s generic argument, the flow through effects have rippled out, with the customer variable and the order variable. The problem is that the IoC container can’t know ahead of time what the implementation type for the interface might be and order.Customer property is forced to use a concrete type (not interface) to satisfy the Entity Framework for mapping purposes enforced by EntityConfiguration<T>. After speaking to David DeWinter (a Tester on the EF Team who responded to a tweet I made), he suggested that it was likely implemented this way to satisfy ObjectSet<T> which also constrains to a concrete implementation (makes sense).
Ultimately the dynamic keyword will get us over the line with respect to this one final hurdle, however my gut says that this work around isn’t entirely in the spirit of NFetchSpec and smacks a little of my old enemy the VARIANT, but I’m prepared to make this one allowance to get me working in a way that suits my needs. I can still read the code somewhat clearly because even when I’m using dynamic for assignment, I can reason about the type based on the interface specified to the repository and the IoC container. I would love to think that this is something that the Entity Framework team might address but I guess I will just have to wait and see.
Under the covers
What code did I need to write to support this ‘adding a product to a customer’ scenario? First up I needed the role itself, then the entities, the mapping classes, the mapping role class and I also needed to write an implementation for IProvideIsolationLevelFor. The Specification I chose to write inline because the parameter value would be provided at run time. Here is the all of it:
The Roles
public interface ICustomerToAddOrder { void AddOrder(IOrderToAddToCustomer order); string CustomerID { get; set; } ICollection<OrderToAddToCustomer> Orders { get; set; } }
public interface IOrderToAddToCustomer { string CustomerID { get; set; } CustomerToAddOrder Customer { get; set; } int OrderID { get; set; } }
The Entities
public class CustomerToAddOrder : IEntity, ICustomerToAddOrder { public string CustomerID { get; set; } public ICollection<OrderToAddToCustomer> Orders { get; set; } public void AddOrder(IOrderToAddToCustomer order) { if (Orders == null) Orders = new List<OrderToAddToCustomer>(); Orders.Add(order as OrderToAddToCustomer); } } public class OrderToAddToCustomer : IEntity, IOrderToAddToCustomer { public string CustomerID { get; set; } public CustomerToAddOrder Customer { get; set; } public int OrderID { get; set; } }
The Mapping Classes
public class CustomerToAddOrderMapping : Mapping<CustomerToAddOrder> { public CustomerToAddOrderMapping(string objectSetName) : base(objectSetName) { Property(c => c.CustomerID).HasMaxLength(5).IsRequired(); HasKey(c => c.CustomerID); Relationship(c => c.Orders).IsOptional(); } } public class OrderToAddToCustomerMapping : Mapping<OrderToAddToCustomer> { public OrderToAddToCustomerMapping(string objectSetName) : base(objectSetName) { Property(o => o.OrderID).IsIdentity(); Property(o => o.CustomerID).HasMaxLength(5).IsRequired(); HasKey(o => o.OrderID); Relationship(o => o.Customer) .FromProperty(c => c.Orders) .HasConstraint((o, c) => o.CustomerID == c.CustomerID); } }
The Mapping Role
public class CustomerToAddOrderMappingRole : IMappingRole<ICustomerToAddOrder> { private IMapping[] _mappings; public CustomerToAddOrderMappingRole() { _mappings = new IMapping[] { new CustomerToAddOrderMapping(“Customers”), new OrderToAddToCustomerMapping(“Orders”) }; } public IMapping[] Mappings { get { return _mappings; } } }
The Transaction Isolation Class
public class AddOrderToCustomerIsolation : IProvideIsolationLevelFor<ICustomerToAddOrder> { public IsolationLevel GetScopeIsolationForRole { get { return IsolationLevel.ReadCommitted; } } }
Piggy in the middle
The issue of managing a short or extended session with an ObjectContext is something that I think a lot of people struggle with. Personally I have never had such an issue because they way I have always worked always involved fetching the most current version of the data I required to have my domain model work against it. This way of working has stayed with me through time immemorial, even in my dark old days of using an anaemic domain model. To be clear I do not pass DTO’s around and reattach them to the ObjectContext, rather refreshing the data and letting the domain model’s business logic take care of the rest.
In Conclusion
My initial planning for the series was that would be take up four posts and therefore it is expected that this may be the last in the series. However, given that Entity Framework 4.0 is still not at RTM and Code Only is still in CTP then it’s possible I may revisit it with a further post sometime in the future.
2 commentsADSD Course with Udi Dahan in Sydney Australia for November 2010
Hot on the heels of Udi’s visit to Melbourne this year (in January), comes another chance to make good if you missed out. Plans are underway for Udi to visit us yet again, this time in Sydney. If your interested check out Udi’s post and register your interest using the link for the proposed Sydney event. If you want to know more about the course you can see the course outline here and read my review which contains links to some further reviews from other attendees in Melbourne earlier this year.
ADSD, NServiceBus and Nuclear Armament – the full story!
After having recently sat through Udi’s 5 day course on Advanced Distributed Systems Design (ADSD) and after several days of quiet introspection punctuated with some random reconnecting to the web full of it’s opinion and ideas, I found myself ever more at peace with the challenges that lie ahead. During a conversation with Udi whilst he was here in Melbourne recently, he recalled a twitter comment made by my colleague Mark wherein he quoted my flippant remark that having NServiceBus at my disposal could be likened to being armed with nuclear weapons. Perhaps it wasn’t the most elegant analogy but I think it conveys the sentiment, I feel able to leverage and harness a great deal energy and power. This sentiment can also be attributed to the many great ideas that you find yourself being exposed to in the ADSD course
In these times of rampant software development technology proliferation, it would be easy to feel overwhelmed and I know from speaking with colleagues that many do, yet somehow I feel more at peace than ever. Technology stacks, frameworks, buzzwords, paradigm shifts, they are all hurtling towards us at a pace most of us have never known and the inertia alone is staggering.
Many of us need help in separating the stuff from the fluff. As Neo said to the architect, “the question is choice”! Well, is it? What are the choices? Do technology choices alone get your system built? No of course they don’t but many have fallen into the trap of behaving as though they do. Ever caught yourself saying something like “oh if I just use MVVM or ASP.NET MVC, my system will be perfect because of benefits X, Y and Z”? Other developers occasionally ask me, “what technology should I learn next” or “how do I future proof myself”? I think they are somewhat surprised by my answer; my advice is to stay as far away as possible from attempting to build value as a vendor stack expert and focus attention on maturing and refining your design and communication skills. It seems obvious really, after all once you have well developed your design and architectural skills, then you are far better placed to make appropriate technology choices. The ensuing implementation experience will sort out any technology knowledge gaps that you feel the job’s market is causing you to feel downward pressure from. This doesn’t mean ignore technology all together, by all means pull it apart and analyze it, see how it fits but not in isolation – you need to give it it’s context. This became quite plain to me after having been through the course two years ago.
The DDD / SOA course was a lynch pin for me it was the key that has opened many doors. Without the learning in principles first, no technology will stop you from making a mess of things. Again it’s pretty obvious when you say it but I am sure I am not alone as someone who came to the course with a whole set of assumptions based on layered designs and technology fads as a bag of best practice tools and tricks.
I have now had the privilege of sitting this course three times and it never get’s old. Since I first experienced it two years ago, the content has changed a little and become even more refined. Each time I sit through the course I feel more empowered than the last time, the lessons are learned more deeply and start to become part of my psyche.
Some of my fellow attendees have posted their thoughts upon having completed the course just recently in Melbourne and I suggest you consider their feedback as well. If you haven’t heard there is now a mailing list dedicated to discussing the courses content and if you are planning on attending in London or the US sometime this year you should definitely check out the mailing list.
I would like to leave you with this; if you have not done this course and you have even the vaguest inkling that you might stand to gain something from learning what what some of the best architects in world know but rarely share in great detail, then do whatever it takes to get yourself a seat when next ADSD rolls into town or find a way of bringing Udi and the course to you.
6 comments







