DarksideCookie

Come to the dark side...we have cookies!

Emancipating your Entity Framework entities

Ok, so first of all, it has been a VERY long and VERY quite period on my blog. I guess that is just the way it is, sometimes life just takes up too much time to be able to blog to the extent I want to. On the other hand, I have been very busy with some cool projects, including some cool technologies, so it is all good I guess.

Now back from the dead I hope that my blogging will pick up, and this is the first in a series of blog posts about things I have figured out during my silent months. I know that Entity Framework is far from what I normally blog about, but it is still something I use, and something I spent some times getting my head around. Understanding EF as such isn’t that hard, the hard part was getting it to work in a fashion that suited me, and one of the things I don’t like is just adding a gazillion repositories on top of EF and be done with it. And I don’t like stepping 15 levels into my objects, lazy loading things as I go along. And I want things to be as DRY as possible. So I prefer having more “emancipated” objects. I want my objects to have freedom to do what they need to do. This makes the programming so much simpler and easier to follow… But it also requires some features like DI… And on top of that, being a bit paranoid, I prefer abstracting away my ORM as much as possible…

So this post is about how I have decided to use EF in my current projects, and I would love any feedback you got…

But before I get started, I want to highlight that this post is not a best practices guide or anything, and the sample I am using is VERY simple. But extending it should be a breeze…

Anyhow, lets get started. I start by creating a new class library project to host the Entity Framework model. In my case I call it DarksideCookie.EF.DIDemo.Data.EntityFramework and the only thing I put into it is an Entity Framework model called StoreModel.

And that is as far as I get before I get a little annoyed. The first thing that happens when you add an EF model is that you get 5 files. An edmx file, a designer file, a diagram and two T4 templates. Actually I don’t mind 4 of those files, but the StoreModel.tt file annoys me. I want to put my entities in a separate project as I will try to abstract away EF. I don’t want to force another library to reference EF to be able to just use my entities…

To do this, I create a second class library project called DarksideCookie.EF.DIDemo.Data. Once that is done, I delete the Class1.cs file and move the StoreModel.tt to this new project. The first issue with doing this is that it will now not find the edmx file that it needs to do its job. So, open the tt-file and locate the row that says

const string inputFile = @"<project name>/<model name>.edmx";

and update the path to point to the edmx file. This generally means adding one or more “../” in front of it, and then adding the name of the project that has the edmx file.

The second issue with moving the tt-file is that it will not be run automatically when you modify the edmx file. So whenever you modify the edmx file, you have to right-click the tt-file and click “Run Custom Tool”, which will run the T4 template and create/update your entities…

Ok, that was my first annoyance with using EF out of the way. The next step is adding functionality to the entities. Once again, this is easily done…and obvious to most people…

As your entities are generated by the T4 template, adding any functionality to the existing .cs files will be futile as they will be rewritten whenever you run the T4 template. But they are declared as partial, so it is actually pretty easy to extend them. Just add a new folder to put your “extensions” in, I name mine “Partials”. Then add a new “class” with the same name as the entity that you want to modify, and make it partial as well. And remember to update the namespace to correspond to the entity you want to extend. Putting the classes in a “partials” folder, will add “Partials” to your namespace…so you have to remove that part…

Imagine a model named that looks like this

image

Assume that we will need a to know the total cost of all the items in the ShoppingCart, which is computed using the items in the cart and their amount and the product cost. This will give us some code like this whenever we need this number

var totalCost = Items.Sum(x => x.Amount * x.Product.Price);

The annoying part with this is that this might be repeated all over the system, which violates DRY. Not to mention that the next step in this post will wreak havoc with this. Instead, we can encapsulate this into the actual entity by adding the following to a cs-file in the same project

public partial class ShoppingCart 
{ public decimal TotalCost
{
get
{
return Items.Sum(x => x.Amount * x.Product.Price);
}
}
}

Now we have a simple way to access the information without spreading out the business logic for calculating the shopping cart total all over the system.

Ok, this is nothing new, but still very important…

So…let’s introduce a change…something obvious…and what is that? Well, the concept of a discount… A discount concept is the reason I didn’t want to store the total in the database, and thus why I didn’t just add a Total property to my entity using the edmx. The total needs to be recalculated continuously as the discounts might be time limited, and thus added and removed at any point of time. So storing the total in the database might mean that a customer gets a discount that has been withdrawn, or don’t get a discount if one has been added, and so on.

Ok, so lets add a Discount object, and add a corresponding property to the ShoppingCart object. The Discount object will have a Name property, as well as a DiscountAmount property.

DISCLAIMER: This is a very ugly way to handle discounts, I know, but it is really simple to show off…so don’t flame me for this very naïve sample…

This means that we can easily update the ShoppingCart to support having a discount by changing the partial to something like this

public partial class ShoppingCart 
{
public Discount CurrentDiscount { get; set; }
public decimal TotalCost
{
get
{
var sum = Items.Sum(x => x.Amount * x.Product.Price);
if (CurrentDiscount != null)
sum = sum - CurrentDiscount.DiscountAmount;
return sum;
}
}
}

Ok, neat. I have added a discount concept that will modify the total of the ShoppingCart, and the code using the ShoppingCart’s TotalCost won’t even need to be changed to get the new functionality.

And if you wanted to, you could obviously add some caching to the discount if that was a heavy thing to figure out…

Next, you need a way to get the Discount object into the ShoppingCart. In my case, since I love DI, I have defined a simple interface called IDiscountService that looks like this

public interface IDiscountService 
{
Discount GetDiscountForShoppingCart(ShoppingCart shoppingCart);
}

and almost the simplest possible implementation of it, which basically adds a 200 discount if the cart contains at least 2 items of the product with id 2 (which in my db happens to be a parachute…)

public class DiscountService : IDiscountService 
{
public Discount GetDiscountForShoppingCart(ShoppingCart shoppingCart)
{
var parachuteItem = shoppingCart.Items.FirstOrDefault(x => x.Product.Id == 2);
if (parachuteItem != null && parachuteItem.Amount > 1)
return new Discount { Name = "Skydiver Deluxe Discount", DiscountAmount = 200 };

return null;
}
}

Ok, so now we have a way to get hold of a discount. But…it needs to be done somewhere… In my demo solution, I use an MVC application, so it will likely be done in the controller. Probably in some fashion similar to this

public ActionResult Index(int? id) 
{
if (!id.HasValue)
return new HttpNotFoundResult();

var shoppingCart = _shoppingCarts.ForUser(id.Value);

if (shoppingCart == null)
return new HttpNotFoundResult();

shoppingCart.CurrentDiscount = _discountService.GetDiscountForShoppingCart(shoppingCart);

return View(shoppingCart);
}

That works nicely… The view displaying the ShoppingCart displays the cart with a discount and everything. Unfortunately, I don’t like solution. What happens if someone forgets to assign a discount to a ShoppingCart in any of all the places the shopping cart is used. That would cause the total to differ, and be very confusing to the customer. I think that the discount concept should be included in the calculation of the total. At least in this simple example…

But…how do we do this? That means that the ShoppingCart object needs to get hold of an instance of IDiscountService somehow. How can we do this?

Well, there are 2 ways. The first one is by using a service locator. Unfortunately, this is frowned upon for a bunch of reasons (which can be discussed back and forth until eternity), so I won’t do that in this case. The other is to get dependency injection going for my entities. Once again, this is not a big thing. it is actually quite simple…even if I have decided to make it a little less simple than it could be…

Just as we can extend EF entities using partial classes, we can extend the DbContext that is generated for us using the T4 template called <Model Name>.Context.tt. Just add a folder to put the partial class in, and add a new class with the same name and namespace, and make it partial. Once you have done this, your can modify the DbContext in any way you want. In my case, I want to add dependency injection…and abstract away EF a bit more…but let’s start by adding dependency injection to our entities…

Being paranoid as I am, I don’t want to get locked into a specific IOC container, so I decided to add my own DependencyAttribute. It looks like this

[AttributeUsage(AttributeTargets.Property)] 
public class DependencyAttribute : Attribute
{
}

as well as an IContainer interface, which abstracts away the IoC container…

public interface IContainer 
{
object GetInstanceOf(Type type);
}

I also decided to add a very simple implementation of the IContainer interface that just uses a Func<T,U> to do its thing

public class GenericContainer : IContainer 
{
private readonly Func<Type, object> _creationCallback;

public GenericContainer(Func<Type,object> creationCallback)
{
_creationCallback = creationCallback;
}

public object GetInstanceOf(Type type)
{
return _creationCallback(type);
}
}

Ok, now that the paranoid part of me has got that out of the way, I can’t get started with the actual DI in the DbContext.

The first thing needed is a new constructor that takes an IContainer as a parameter, and calls the “original” DbContext’s constructor, and thus making sure that my DbContext get’s set up properly.

public partial class StoreModelContainer : IDataContext 
{
private readonly IContainer _container;

public StoreModelContainer(IContainer container) : this()
{
_container = container;
}
}

And now, before I go further, I want to make sure that it isn’t possible to call the other constructor by chance. This is easily done by modifying the <Model Name>.Context.tt file, changing the constructor from public to protected.

protected <#=code.Escape(container)#>() 
: base("name=<#=container.Name#>")

Now that I force everyone to use my new constructor, I can go ahead and get the DI going. The DbContext class, which my container inherits from, implements an interface called IObjectContextAdapter. This interface gives me access to a lot of interesting things, including an object state manager that can be used to get notified whenever the object collection is changed. This includes when and entity is added to the collection, which basically means whenever an entity is retrieved from the database. This is what I am interested in. So I hook into that event and make sure that I only do things to the object when it is added to the collection and the entity’s state is unchanged (new).

public StoreModelContainer(IContainer container) : this() 
{
_container = container;
((IObjectContextAdapter)this).ObjectContext.ObjectStateManager.ObjectStateManagerChanged += ObjectStateManagerChanged;
}

protected void ObjectStateManagerChanged(object sender, CollectionChangeEventArgs e)
{
if (e.Action != CollectionChangeAction.Add)
return;

var manager = sender as ObjectStateManager;
if (manager == null)
return;

var state = manager.GetObjectStateEntry(e.Element).State;
if (state != System.Data.EntityState.Unchanged)
return;

FullfillDependencyRequirements(e.Element);
}

The implementation of the FullfillDependencyRequirements method is not very complicated, it is just a bit of reflection. The only issue is that EF returns dynamic proxies (which inherit from the defined entities), which are a bit interesting to work with when it comes to reflection. So my solution to this is to check if it is an dynamic proxy, and in that case get the base type, which will be the actual entity type. After that, I just get all properties adorned with the DependencyAttribute and inject the dependency using my IContainer.

private void FullfillDependencyRequirements(object entity) 
{
var entityType = entity.GetType();
if (entityType.FullName.StartsWith("System.Data.Entity.DynamicProxies")) entityType = entityType.BaseType;

var props = entityType.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic);
foreach (var prop in props)
{
var attr = (DependencyAttribute)prop.GetCustomAttributes(typeof(DependencyAttribute), true).FirstOrDefault();
if (attr == null)
{
continue;
}

var dependencyObj = _container.GetInstanceOf(prop.PropertyType);
prop.SetValue(entity, dependencyObj, null);
}

That is all there is to it!

The last thing I want to do before being satisfied, is to abstract away the DbContext by adding a new interface that looks like this

public interface IDataContext 
{
IQueryable<ShoppingCart> ShoppingCarts { get; }
IQueryable<Product> Products { get; }
}

and implementing that interface in the custom partial class like this

public partial class StoreModelContainer : IDataContext 
{

public IQueryable<ShoppingCart> ShoppingCarts { get { return ShoppingCartSet; } }
public IQueryable<Product> Products { get { return ProductSet; } }
}

Now that my DbContext has been abstracted away, I got DI going and I have emancipated my ShoppingCart object, using the system is a breeze.

Setting up the IoC container using Ninject looks like this

kernel.Bind<IContainer>().ToConstant(new GenericContainer(x => kernel.Get(x))); 
kernel.Bind<IDataContext>().To<StoreModelContainer>().InRequestScope();
kernel.Bind<IShoppingCarts>().To<ShoppingCarts>().InRequestScope();
kernel.Bind<IDiscountService>().To<DiscountService>().InRequestScope();

and the Controller becomes really simple and nice

public class ShoppingCartController : Controller 
{
private readonly IShoppingCarts _shoppingCarts;

public ShoppingCartController(IShoppingCarts shoppingCarts)
{
_shoppingCarts = shoppingCarts;
}

public ActionResult Index(int? id)
{
if (!id.HasValue)
return new HttpNotFoundResult();

var shoppingCart = _shoppingCarts.ForUser(id.Value);

if (shoppingCart == null)
return new HttpNotFoundResult();

return View(shoppingCart);
}
}

OMG, that was a lot of information in a single blog post, but those of you who have been to my blog before knows that that is how I roll.

And on top of getting a long overdue blog post up on my blog, I have also managed to use the acronym OMG for the first time evah… Smile

Demo project is available as usual. However this time it is created in VS2012, but if you want it in VS 2010, just let me know and I will try to back-port it… 

Source code: DarksideCookie.EF.DIDemo.zip (278.06 kb)

Posted: Oct 18 2012, 15:48 by ZeroKoll | Comments (0) |
  • Currently 0/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5
Manage post: :)

Add comment




  Country flag
biuquote
Loading