http://blogs.clariusconsulting.net/kzu

Daniel Cazzulino's Blog

Go Back to
kzu′s Latest post

How to design a unit testable domain model with Entity Framework Code First

I got this question a couple times already so I’ll save keystrokes and post here.

I don’t like the Repository pattern too much either, especially since the advent of IQueryable and Linq, which make it more of a burden than anything else and hinders usage of cooler things on top of lambdas and expressions.

The new EF 4.1 Code First API, specifically DbContext, fits almost perfectly with this new way of doing domain modeling.

What I need from my domain “context” class is:

  • Add/Save/”Delete
  • Expose IQueryable for the aggregate roots
  • Allow to eagerly load relationships (a.k.a. Include or Fetch)

And of course it needs to be testable/moq-able.

So my main interface look like this:

public interface IDomainContext
{
    void SaveChanges();

    void Save<T>(T entity) where T : class, IEntity;

    void Delete<T>(long id) where T : class, IEntity;

    IQueryable<Product> Products { get; }
    // Other aggregate roots.
}

My IEntity is pretty simple and just provides a way to consistently handle lookup and logical deletes:

public interface IEntity
{
    long Id { get; set; }
    bool IsDeleted { get; set; }
}

You can of course use other identity types/strategies, that’s not important at this point.

One critical thing at this point is the Include. This is an extension method over IQueryable<T> provided by the DbExtensions class in EF 4.1. So how can we possibly mock it?

First thing first: consumers of our domain context will never have an import of the System.Data.Entity namespace. Ever. They don’t care about IDbSet, Database, DbContext, etc. So the extension method will never be in context for them.

We’re free to re-implement Include therefore using my favorite approach:

public static class QueryableExtensions
{
    internal static IIncluder Includer = new NullIncluder();

    public static IQueryable<T> Include<T, TProperty>(this IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class
    {
        return Includer.Include(source, path);
    }

    public interface IIncluder
    {
        IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class;
    }

    internal class NullIncluder : IIncluder
    {
        public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path) where T : class
        {
            return source;
        }
    }
}

As you can see, this includer will do nothing by default when running on unit tests (if we leave the default or assign the NullIncluder explicitly). And in our DbContext-based DomainContext implementation, we set it to one that does the EF thing:

internal class DomainContext : DbContext, IDomainContext
{
    static DomainContext()
    {
        QueryableExtensions.Includer = new DbIncluder();
    }

    private class DbIncluder : QueryableExtensions.IIncluder
    {
        public IQueryable<T> Include<T, TProperty>(IQueryable<T> source, Expression<Func<T, TProperty>> path)
            where T : class
        {
            return DbExtensions.Include(source, path);
        }
    }
    ...
}

Piece of cake. The rest of the EF domain context is also quite trivial:

internal class DomainContext : DbContext, IDomainContext
{
    ...
    public virtual DbSet<Product> Products { get; set; }

    void IDomainContext.SaveChanges()
    {
        this.SaveChanges();
    }

    IQueryable<Product> IDomainContext.Products
    {
        get { return this.Products.Where(x => !x.IsDeleted); }
    }

    public void Save<T>(T entity) where T : IEntity
    {
        this.Set<T>().Add(entity);
    }

    public void Delete<T>(long id) where T : IEntity
    {
        var saved = this.Set<T>().Find(id);

        saved.IsDeleted = true;
    }
}

Now our tests can mock the IDomainContext and the business logic can go crazy with it and we can have all in-memory objects for the context.

Here’s an example of business logic that can now be unit-tested without the database:

// Shows how we're unit testing some piece of business logic
// without a real database and using the IDomainContext as
// the entry point for mocks.
public class StoreSpec
{
    [Fact]
    public void WhenProductToPurchaseDoesNotExist_ThenThrowsNewArgumentException()
    {
        var products = new[]
        {
            new Product { Id = 1 },
        };

        var context = Mock.Of<IDomainContext>(x => x.Products == products.AsQueryable());
        var store = new Store(context);

        Assert.Throws<ArgumentException>(() => store.Purchase(2, 1));
    }

    [Fact]
    public void WhenProductToPurchaseExists_ThenIncrementsSalesAndSavesChanges()
    {
        var products = new[]
        {
            new Product { Id = 1, Sales = 25 },
        };

        var context = Mock.Of<IDomainContext>(x => x.Products == products.AsQueryable());
        var store = new Store(context);

        store.Purchase(1, 5);

        Assert.Equal(26, products[0].Sales);
        Mock.Get(context)
            .Verify(x => x.SaveChanges());
    }
}

Due to popular demand, now you can download the sample solution with the tests Winking smile

Comments

91 Comments

  1. [...] How to design a unit testable domain model with Entity Framework Code First & How extension methods ruined unit testing and OOP, and a way forward – Daniel Cazzulino takes a look at building a domain model using Entity Framework Code First in a test friendly way, and also takes a look at a technique for managing extension method implementations, looking at splitting the contact of the method and the implementation. [...]

  2. Hi Kzu!

    I was a fan or Repository pattern in “before EF code first” ages…but recently I read a good discussion about DbContext is your UnitOfWork and DbSets are your repositories, and I totally agree, although adding a project specific “API” for the data access operations to take care of “repeatative” linq expressions specially .Where-s is still applicable. The question is what pattern are you fitting it into, how do you group them.

    What do you think? How are you doing it nowadays?

    Are you about to post a complete “moq-ed” solution for the above stuff? (Which I really like)

    Thanks,
    Attila

  3. Code first allows your tests to drive the design – you say you need Add/Save/Delete, but until you’ve written your tests, how can you know that?

  4. Yes, first time the repository pattern works fine out of the box in EF :) . The name Save does not look intuitive for me as it makes me think you need to call it every time you need to persist a change in the entity, but I guess it is a personal taste.

    BTW, this blog module is a crap. If you submit a comment without specifying the captcha, you don’t get any client validation and the comments dissapear.

    Pablo.

    • Disabled the captcha module altogether. Thanks for the feedback Pablo :)

      Yes, Save is weird, but unless you loaded the entities from the context itself, you always have to call it anyways (i.e. from a WCF endpoint).

  5. Having a slow day, what does Entity map to in your DomainContext class?

    • Entity is just a base class (could be an interface too) that simply has an Id. Needed to enable generic handling of Save/Add/Delete using the entity Id.

  6. [...] An initial problem with some of these implementations is that they didn’t make certain abstractions implicit making really hard to replicate their behavior with mocks or stubs as part of an unit test. A typical example was the the “eager loading” capability of EF in the initial versions. It was not possible to use lazy loading for associations, and the Include method for loading those was not something you could easily abstract as part of the repository. While this issue was partially addressed with POCOs, only the latest EF code first bits makes the approach of making an unit testable  repository something possible. [...]

  7. In this model I do not see how DbContext is abstracted from the user. You would still need a Unit Of Work style interface so they didn’t have to reference the entity framework. That being said, am I missing something?

  8. You’re missing that the user just references IDomainContext. The Unit of Work style is SaveChanges().

  9. I’m liking the concept. Do you have a downloadable example project of this we could have a look at please?

  10. Like the concept and work, thanks. +1 on downloadable sample.

    Maybe I’m missing something but it seems like there is an inconsistency between Save and Delete being constrained differently between IDomainContext and DomainContext with IEntity vs Entity which doesn’t compile:

    The constraints for type parameter ‘T’ of method ‘DomainContext.Save(T)’ must match the constraints for type parameter ‘T’ of interface method ‘IDomainContext.Save(T)’. Consider using an explicit interface implementation instead.

    P.S. The black and dark gray color combos are a little hard to read on the blog, especially in comments.

    • Thanks for the feedback. I’ll pass it on to the gx guy :) .
      I find it very useful to Ctrl++ on the page to get bigger fonts (in many blogs).

      I like this style as it’s easier on the eyes (at least for me).

  11. Fixed constraint to IEntity (was just a typo).

    There isn’t anything to download, because all the code is already in those <30 lines of code I show here…

  12. DomainContext Save and Delete cannot be constrained just to IEntity though because that’s a different compiler error:

    The type ‘T’ must be a reference type in order to use it as parameter ‘TEntity’ in the generic type or method ‘System.Data.Entity.DbContext.Set()’.

    If you convert them both to Entity then that sort of misses the point and any other way generates a compiler error one way or the other. This is one reason I was +1 on the sample project because if you take the code here, it simply doesn’t compile.

  13. :) Thanks. Yeah, same changes I made. It’d still be cool be cool to see complete samples with a unit test project but sure I can figure that out.

    Was previously looking at http://blogs.msdn.com/b/adonet/archive/2009/12/17/walkthrough-test-driven-development-with-the-entity-framework-4-0.aspx and http://thedatafarm.com/blog/data-access/agile-entity-framework-4-repository-part-6-mocks-amp-unit-tests/ but those are more repository style.

    Anyway, thanks again. Nice work

  14. Too good man , this is exactly what for … thanks a ton

  15. One problem I had at runtime with this approach is below:

    System.NotSupportedException occurred
    Message=The specified type member ‘IsDeleted’ is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.
    Source=System.Data.Entity

    It was the same problem with the Id property. Now if I directly used my real DbContext the problem went away. Using it via the interface however, Entity Framework seems to need IDbSet on the interface, IQueryable alone was no good. So in this example of yours, IDbSet Products { get; } on DomainContext instead of IQueryable Products { get; }.

  16. Geoff: I explicitly didn’t want to take a dependency on EF, so that’s a no-go.

    I uploaded a sample solution now where you can see it working.

    Thanks for the feedback :)

  17. [...] DbContext I like Kzu's take on building unit-testable domain models with EF code first. I've been playing around with some of the same ideas myself, which center around simple context [...]

  18. Nice work buddy! How do you run the test using Visual Studio 2010? Can I use these in integration with a Continuous Integration build (with TFS)

    Thanks!

  19. @Attila Do you have a link to the article?

  20. Hi Daniel,

    In the test method WhenDeletingProductThenCanNotReadIt, change the IDomainContext with “var” and run the test. (The assertion fails, but this is expected since you are creating an explicit interface
    method implementation of IDomainContext’s IQueryable in the DomainContext class). But beyond this, the post and the provided example is very good.

  21. Isn’t there a problem with differences between linq-to-object and linq-to-entities when using IQueryable in the controller while mocking the context?

  22. Sure, that’s why you always need integration tests too. But those are more like end-to-end/acceptance testing.

    And you can still enjoy the speed boost from a unit-testable counterpart. In my experience (at least with EF 4.1), you can detect the differences easily by just trying to instantiate your DbContext-derived class (your “mapping” of entities to the underlying ORM, if you will). At that point, any unsupported structure or mapping will cause an exception, so no integration test will actually pass.

    Typically I just keep around a simple integration test that instantiates the ORM context and just does a Count on each of the entity sets. This covers the vast majority of unsupported differences with linq-to-objects, I’ve found.

  23. This is a very nice post. I’m still trying to get my head around all of it. The biggest question I have involves exposing Products as IQueryable on IDomainContext. I understand that alleviates the need for clients to take a dependency on System.Data.Entity, but wouldn’t it result in the database executing “SELECT * FROM Products” when clients access the Products property on an implementation of IDomainContext? It seems to me every product would be returned only to be filtered in a linq query against IQueryable. Am I fundamentally misunderstanding how that works?

    Thanks!

  24. Brice: yes, you’re missing that queries/order/select on an IQueryable are deferred and only executed when you actually enumerate the results, at which point the particular EF provider will translate the query into a SQL query to the database.

    So, no, you’re never filtering in-memory a “SELECT * FROM Products”. Reading a bit more about EF and Linq providers in general would help to understand what’s going on.

    • Yes. I knew execution against IQueryable was deferred until enumeration. I just didn’t realize it was smart enough to combine the filters applied by the client to the exposed IQueryable property (“Product”) with the filters used inside the accessor for the exposed property, e.g. Where(x => !x.IsDeleted), before passing it to the database. I did a quick test and peeked at the generated SQL and it did in fact work exactly as you said. Very cool. Thanks for clearing that up for me.

  25. Thanks Daniel, this is exactly what I wanted to include in the project I am working for.
    I managed to get this working with a database first EF context. I had to tweak the EF context T4 template to include the IDomainContext implementation. I also used a similar T4 template to generate the IDomainContext interface declaration. Every time I update the EF context edmx file the IDomainContext is also updated – this is very nice.
    I now have my entity classes and the business logic in assemblies that don’t have any reference to EF making the solution cleaner and easier to understand and test.

  26. kzu, great post! how do you handle eager loading when your need the eager loaded entities to be filtered down (in the actual sql query)?

    • EF already handles that.
      Say you have a queryable of Product that contains Release entities, and you only want to load all releases for v1 of the product, you just make a projection:

      var v1Releases = context.Products.Select(x => new { Product = x, Releases = x.Releases.Where(r => r.Version == 1) });

      (haven’t sql-profiled it, but I think it would only retrieve the product and the requested releases, not all of them)

  27. I was thinking in the terms of the Include extension method when IDbContext is exposing only Aggreates.

    • EF automatically includes properties that you access via the Where predicates, AFAIK, so no need to do that explicitly.

      Is there a way to do what you want with “plain” EF? If there is, then there may be an opportunity to improve this domain context thinghy…

  28. I have a fundamental problem with calling something loaded directly from the database a “Domain Entity”. They’re Data entities, not Domain entities.

  29. Your information is very useful. I will subscribe to your blog to get dailly updates.

  30. kzu,

    How do you resolve the problem when you need to use one LINQ statement a few times?

    Thanks

  31. I absolutely love this blog. Could tell me how I can keeping up to date with it?

  32. I ran across some excellent types here as effectively.

  33. I have added this method to Store class:

    public int ProductCount()
    {
    return this.context.Products.Count();
    }

    and this test method to StoreSpec:

    [Fact]
    public void WhenDeletingProduct_ThenCanNotReadIt()
    {
    var products = new[]
    {
    new Product { Id = 1, Title = “WoVS”, IsDeleted = true },
    };

    var context = Mock.Of(x => x.Products == products.AsQueryable());
    var store = new Store(context);
    var productCount = store.ProductCount();
    Assert.Equal(0, productCount);
    }

    but assertion is failed though I have already deleted product. What am I doing wrong?

    Thank you

    • Your Count implementation is wrong. It should take into account that logically deleted products shouldn’t be counted:

      public int ProductCount()
      {
      return this.context.Products.Where(x => !x.IsDeleted).Count();
      }

  34. [...] have ran into many articles about using fake DbContext, such as here, here, and here but they all seem to have some pros and cons. One group of people say that one should not [...]

  35. I downloaded the test code sample and having difficulty running and debugging the tests. The project states that there is no test loaded. What am I doing wrong?

    • Great posting!

      I’m new to EF and I’ve been coding to the repository pattern and kind of hating it. This might seem like a dumb question, but how is your EF connection string being specified? Is it just assuming defaults, as a database does get created in SQL?

      In implementing this approach, would I expose all of this directly to my BLL, or would I do some encapsulation (is that what your doing with the Store class)? Where can I find more information on your approach (I know, bing/google, but do you know of any examples projects that use this approach)?

      BTW- I would think that Delete would be acceptable when trying to kill off SPAM posts.

    • Totally meant to post that in a new thread.

      I was going to tell Alex that the tests were in xUnit, not MSTest.

  36. Hi Daniel

    I see you have created nuget packages Domain Context for Entity Framework and Domain Context for Entity Framework xUnit Tests. I have tried to use them but no success yet. Please can you write me small instructions – I have existing SQL database, created small console application and added EMDX. Created separate test project. Now what? Which package(s) need I insall in each project? How to start to use them both in real application connected to DB and in tests (disconnected)?

    Maybe you already have some documentation. I tried to find but no lack.

    Thank you

    Alex

    • Domain Context currently is for code-first only. It could be adapted for database-first EDMX usage, but I haven’t had the need…

  37. I love your model, but any reason you use the term ‘Save’ for a method that actually ‘adds’? It seems unintuitive to me and I was wondering if you have a particular reason, maybe something DDD related.

    • That was just a simplifying explanation. A proper “Save” for a context has to detect new as well as updated entities, and in order for it to be completely transparent to business logic, it should also do collection add/remove of nested child elements and so on (i.e. like NHibernate does OOB).

      That’s what the full version does (see http://kzu.to/PRfWAs), and I’ve since renamed Save to Persist.

  38. There’s one problem. You properties have type IQueryable instead of IDbSet. This means you can’t do context.Products.Find(). You need to do context.Set().Find() instead.

    • That’s not a problem, on the contrary, it’s absolutely by design. I don’t want to depend on an EF type like IDbSet. Find doesn’t gain you anything over .FirstOrDefault( .. find by primary key property ). Go and run it with a SQL profiler. I’ll wait ;) .

      So, why use an abstraction you don’t need?

  39. @Jim: the connection string by convention (of EF) can be specified in the app.config/web.config with a name equal to the full name of the dbcontext-derived class, and it will just pick it up. Otherwise, you can specify a friendlier name, which also has to match a connection string in the config file with the same name.

  40. I like that consumers of our domain context will never have an import of the System.Data.Entity, but just to check whether I understood correctly: consumers still will have to add a reference to EF since DomainContext derives from DbContext, correct?

  41. Of course… I’m new to this, and I had to reference Entity Framework when configuring the DI container (StructureMap), but I suppose this is ok because that’s the composition root ?

    • Indeed. The boostraping code always takes dependencies on all concrete implementations. And that’s OK.

      You could workaround it by having some sort of dynamic discovery mechanism, like setting up the DI container by looking at all assemblies in a certain folder and so on, though.

  42. Thank you expressing your thinking on this weblog site.

  43. Exactly what I used to become seeking, and incredibly nicely spelled out.

  44. Thought it was truly detailed. I came across this internet site on Yahoo, cheers a good deal.

  45. I really like the idea and I think I understand it conceptually but not the implementation.

    How is the Include methods on DbIncluder called? For example, I’m looking at the test project, DomainContextSpec.WhenCreatingProduct_ThenCanSaveIt, line 30. context.Products.FirstOrDefault is being called. In the actual application (not the test project), would FirstOrDefault also be called or would the DbIncluder.Include extension method that takes in an expression be called?

    Thanks for any help.

    • Nevermind, I realized the answer. For those who was as confused as I was: The purpose of this (as stated in the article) is to create an interface of extension methods that are part of entity framework and then use these interfaces in your service layer or application. The include extension methods are used for eager loading for entity framework. The other way to query entity framework is via the extension methods available as part of System.Linq and thus is ALREADY independent of entity framework.
      What was confusing to me was two things: 1) we don’t need to make another interface for the lazy loading way of doing things because that’s in System.Linq. 2) the sample didn’t include the usage of the include methods. In real life (aka your application) it would actually would call the include methods via the interfaces.

  46. How is the Include methods on DbIncluder called? For example, I’m looking at the test project, DomainContextSpec.WhenCreatingProduct_ThenCanSaveIt, line 30. context.Products.FirstOrDefault is being called. In the actual application (not the test project), would FirstOrDefault also be called or would the DbIncluder.Include extension method that takes in an expression be called?

    Thanks for any help.

  47. Thank you for posting this.. 2 years and 1 major version of EF down the road, and I still come back to this particular blog post as a reference. I have even taken time to modify the standard T4 templates used with edmx files to generate the interface and class implementation required to make this approach work.

  48. @kzu, thanks for this. I’m getting an ambiguous invocation error when I try to use the “Include”. It can’t differentiate between the Include extensions in class DbExtensions versus your DbIncluder : QueryableExtensions.IIncluder. Am I missing something? thanks, -Dave

  49. I solved my issue, but haven’t exactly figured out why it was an issue for me which doesn’t exist in the sample solution. I thought maybe because I’m using Entity Framework 5, but that shouldn’t be an issue because the DLL in question is System.Data.Entity. Anyway. Basically the compiler was confused by the “Include” in this solution, versus System.Data.Entity.DbExtensions. My solution was to comment out the “using System.Data.Entity” at the top of the DomainContext.cs file, then explicitly reference (e.g) System.Data.Entity.DbContext, System.Data.Entity.DbSet, etc. Then the “Include” correctly points to the one in KZU’s “QueryableExtensions”.

  50. Maybe this should be obvious, but it wasn’t to me, because I had put the QueryableExtensions in a different namespace, which caused me a moment of confusion. Just to make this even more clear, with respect to @Andy Cheng’s comments above… (and because there is no real example of a call to the “Include” extension method)… if you want to call “Include”, to do eager loading, you need to ensure you’ve got a reference to the assembly that contains QueryableExtensions, in the calling class, e.g. your MVC controller; then you need to apply it to one of the IQueryable collections in your concrete implementation of IDomainContext. For example (if you’ve got a “Product” entity, which has a “Category” navigation property that points to another EF entity):

    var productsWithCategory = DomainContext.Products.Include(x => x.Category);

  51. Alternatively, you can download my project on Codeplex. I have a working example that you can copy.
    https://entityinterfacegenerator.codeplex.com/

    It generates the interface files that you need for IoC purposes.

  52. I wanted to find out if you have revisited your implementation post EF6 and if so, would you make any changes?

  53. Allan +1. Is this still valid for ef 6? Any other changes to do ?