Home > Design Patterns, Entity Framework, ORM > The Entity Framework and a unified Lazy and Eager loading framework.

The Entity Framework and a unified Lazy and Eager loading framework.

I am in the middle of spiking a solution that aims to unify an approach to lazy and eager loading with the Entity Framework. I was ready to give up on the idea a while back but I am a stubborn PITA. It has been my goal for a while to try get Specifications, Repositories and (finally) Fetching Strategies bundled into a single approach for the Entity Framework. What I want to be able to do is write code like this:

public void Multi_Level_Mixed_Hierarchy_With_Persisting_Test()
{
    EntitiesRepository<Customer, NorthwindEntities> repos =
        new EntitiesRepository<Customer,
            NorthwindEntities>(new NorthwindEntities());

    Specification<Customer> german_cust_spec =
        new Specification<Customer>(c => c.CustomerID == “ALFKI”);

    var german_custs = repos.AllAsIList(“Customers”, german_cust_spec, new
                        MultiLevelMixedStrategy());

    foreach (var cust in german_custs)
    {
        cust.Country = “Italy”;
        Console.WriteLine(“The Customer Name is {0}”, cust.CompanyName);
        foreach (var order in cust.Orders)
        {
            order.ShipName = “The big boat”;
            Console.WriteLine(“\tThe Order ID is : {0}”, order.OrderID);
            foreach (var orderline in order.Order_Details)
            {
                orderline.Discount = 0.13f;
                Console.WriteLine(“\t\tThe value ordered for ” +
		    “Product ID {0} is {1}”,
                    orderline.Product.ProductID,
                    orderline.UnitPrice * orderline.Quantity);
            }
        }
    }

    repos.Session.SaveChanges();
}

The Repository and the Specification aspects to this unified approach have previously been developed and documented but the missing piece that has been outstanding is the Fetching Strategy. You can see above in the call to the repository .AllAsIList() method, that the third argument is the Fetching Strategy of which I speak.

public interface IFetchingStrategy
{
    IList<FetchingIntention> Intentions { get; }
}

public interface IFetchingStrategy<TRole> : IFetchingStrategy { }

And the implementation is:

public class MultiLevelMixedStrategy :
    IFetchingStrategy
{
    private readonly IList<FetchingIntention> _intentions =
        new List<FetchingIntention>();

    public MultiLevelMixedStrategy()
    {
        this.Intentions.Add(new FetchingIntention(“Orders”,
                                FetchMode.Eager));
        this.Intentions.Add(new FetchingIntention(“Orders.Order_Details”,
                                FetchMode.Eager));
        this.Intentions.Add(new FetchingIntention(“Product”,
                                FetchMode.Lazy));
    }

    public IList<FetchingIntention> Intentions
    {
        get { return _intentions; }
    }
}

Where the Entity Model looks like this:

ef_with_box_and_dice

So far the results look promising. I will have more to say on this shortly and with a bit of luck will be releasing the code which rounds out the triad of a Repository, dynamic querying capability via Specifications and Fetching Strategies for a unified approach for lazy and eager loading that does NOT require you ever explicitly call the .Includes(), Load() or IsLoaded methods and property on the EntityCollection<T>, EntityReference<T> or ObjectQuery<T> objects.

Share/Save/Bookmark

  1. February 12th, 2009 at 02:05 | #1

    Simon:

    I like the idea and I’ve been working with something similar. Couple questions:

    - What if you used IFetchingStrategy to get strongly typed code? Like: new FetchingIntention(c => c.Orders, FetchMode.Eager). Using an Expression as the first parameter would make it relatively easy to dig the out a string value for the property name(s) and then .Include() it in EF. The benefit being you never have to worry about magic strings.

    - Curious: why do you need “AsIList” as a suffix on the repository method?

    Looking forward to more.

    [Reply]

  2. February 12th, 2009 at 06:52 | #2

    Hi Scott

    Yes the magic strings were a smell on my radar and thanks for the neat solution, the expression is much better and in keeping with one of reasons I was keen to keep at it with EF (LINQ).

    The AsIList() method will possibly be accompanied by an AsIQueryable() and perhaps even an AsObjectQuery() overload so I am not forcing IList’s on the user. IList is my preferred way of working however as much of my data access work will occur in a service layer and I wont be holding on to long lived ObjectContext’s.

    Thanks,

    Simon

    [Reply]

  3. February 12th, 2009 at 08:21 | #3

    Simon,
    I can’t tell you how much I’m waiting for the follow up artice after the tease at the end there.
    I’ve got round the lazy load at the moment using aspects but I’d rather not have to do it if at all possible.
    The lack of lazy load is the one thing that really irks me about EF, for me it’s correctness first, optimization second. I know lazy relations are network calls, I don’t need to write more code to remind myself.
    Keep up the great posts - they’re appreciated,
    Paul

    [Reply]

  4. February 12th, 2009 at 12:16 | #4

    Paul

    I’m afraid I wont be relieving you or anyone else from using an ASPECT. After looking at your implementation for lazy load just now, I can tell you that mine is very similar and I too used postsharp to achieve it. I share your frustration and agree with all your points re the rationale of why we have been pushed down this path with EF and why we would prefer it weren’t so. What I am trying to achieve in the broader sense, is to enable myself with a framework that will allow me to unify how I build my repositories, apply dynamic querying (via the Specifications) and declare my intent explicitly about fetching (eager or lazy). Whilst lazy loading has been enormously frustrating I equally do not wish to pollute my business logic with eager loading instructions. If I place all my business logic for an entity in my partial class that extends the EF entity, I don’t want any infrastructure code in there at all and I want my methods to know explicitly what to load and not load. This is achieved by using Roles (an approach I picked up from Udi Dahan). I can use generic method argument to express my role under which I request my business entity to behave. Perhaps best exemplified by way of an example?

    public interface ICustomerMakePrefered
    {
         public void MakePrefered;
    }
    
    public partial class Customer : ICustomerMakePrefered
    {
        public void MakePrefered()
        {
            // I should eager fetch becuase
            // I expect that I might get thousands
            // of orders and lines.
            // My pre-loaded fetching strategy has
            // taken care of this for me ahead of time.
            foreach (var order in this.Orders)
            {
                foreach (var orderline in order.OrderLines)
                {
                    orderline.price -= (orderline.price * 0.1);
                }
            }
        }
    }
    
    public class MarketingService
    {
        public void MakeCustomerPrefered()
        {
            Repository<Customer> cust_repos =
                new Repository<Customer>();
            //get the relevant customer
            //and set the entity so it knows
            //what it’s fetching strategy should
            //be based on the explicit role of
            //making it prefered
            var cust = cust_repos.Get<ICustomerMakePrefered>(“ALFKI”);
            //make the customer prefered 
            cust.MakePrefered();
        }
    }
    
    public class Repository<TEntity> where TEntity : class, new()
    {
        public TEntity Get<TRole>(string custId)
        {
            // get the instance of T and finds (via reflection) 
            // any assemblies that contain a loading strategy
            // that implements TRole. When using EF to get
            // the Entity via the ObjectContext, I can use
            // the found Fetching Strategy to load appropriately.
            // This allows me to drop new FetchingStrategies in
            // and out at will.
            //…… use ObjectContext to get setup my query
            //PLEASE NOTE: this is not the exact implementation
            //of my repository.
        }
    }

    [Reply]

  5. February 13th, 2009 at 06:37 | #5

    Hi Simon,

    It all looks really nice, I’ve been following your blog for a while now so I’ve seen your journey to this point. The approach you’ve taken seems really clean, I really do like the way you can tune the loading strategies on a use-case by use-case basis.

    Paul

    [Reply]

  6. February 14th, 2009 at 08:28 | #6

    Simon,

    Very elegant. I hope the next version has an update to the Fetching Intention to remove the magic strings.

    Mark

    [Reply]

  7. Pieter
    August 8th, 2009 at 17:24 | #7

    Hello Simon,

    Thank you for your great work!

    I searched for the code of the remarkable .AllAsIList method. But I can’t find that. Where can I find it?

    With friendly greetings,
    Pieter
    The Netherlands

    [Reply]

  8. August 8th, 2009 at 22:56 | #8

    Pieter

    Code is in a subversion repository. I am following with an Entity Framework 4.0 version, geared to take advantage of POCO. In the meantime, the code your looking for can be downloaded from there.

    http://simonsegal.net/TechAPublicRepo/Trunk/EntityFramework-Repo-Fetching-Strategies

    [Reply]

  9. Pieter
    August 9th, 2009 at 02:37 | #9

    Hello Simon,

    Thank you for your quick answer. But at the referenced location I can’t find the code for
    AllAsIList(“Customers”, german_cust_spec, new
    MultiLevelMixedStrategy());

    With friendly greetings,
    Pieter
    The Netherlands

    [Reply]

  10. August 9th, 2009 at 11:26 | #10

    Pieter

    are you looking for the Test displayed in the post that contains that method call to the repository? Or are you looking for the implmentation of the AllAsIList method - in the repository itself?

    The test (as shown in this post) is in the program.cs file in the Org.Techavalanche.Orm.Tests.Domain project and the AllAsIList implmentation is in the EntityFrameworkRepository.cs file in the Org.TechAvalanche.Orm.Repository project. The implementation of the fetching strategy being used in that example (MultiLevelMixedStrategy) is located in the FetchingStrategies.cs file in the Org.Techavalanche.Orm.Tests.Domain project.

    I have downloaded the code myself just now and confirmed that they are all there. I hope I have understood your question correctly?

    Good luck and thanks,

    Simon

    [Reply]

  1. February 22nd, 2009 at 16:01 | #1
  2. February 23rd, 2009 at 19:23 | #2
  3. June 11th, 2009 at 00:06 | #3

Creative Commons Attribution-ShareAlike 2.5 Australia
Creative Commons Attribution-ShareAlike 2.5 Australia