Typed Eager Loading Using Entity Framework (& What is Eager Loading vs Deferred Loading)

If you don’t know what eager loading is, Jump to “What’s eager loading?”.

Eager Loading Syntax

If you are eager loading Products for example in a typical (Categories 1<->* Products) relation, the standard syntax would like:

DbDataContext.Categories.Include(“Products”)

What is the problem with that?

The “Products” part. The word “Products” is a string. If I rename the Products table to ShopProducts or whatever or even remove it from this data diagram and have it elsewhere, or even something wrong happens and the relation is removed from DB/diagram by mistake, my code will still compile, but will fail when it runs. This is BAD BAD BAD.

How to solve this?

Since I always believe that if something exists somewhere you shouldn’t do it yourself unless its totally broken (and I mean REALLY REALLY BROKEN), I started searching inside the Entity Framework itself for something to get the entity name from.

At first it seemed super easy. Every entity class has a static property “EntityKeyPropertyName”, so, I thought I can write something like:

DbDataContext.Categories.Include(Product.EntityKeyPropertyName); // But this didn’t work

Where Product is the entity class generated for table “Products”. Note that singularizing the name (Products becomes Product) does not happen automatically like in Linq-To Sql, you’ll have to change it manually, which is not required for the code here of course.

As you an see n the comment, this didn’t work. The value of property was always “-EntityKey-”, the default value of the abstract class “StructuralObject” which all entity classes inherit.

I kept searching all over until I found that the only place I can get the name from was an Attribute generated on the class somewhat like this:

[global::System.Data.Objects.DataClasses.EdmEntityTypeAttribute(NamespaceName=”DatabaseNameFlowModel”, Name=”Products”)]

My requirement was simple. if the diagram has something wrong that the relation between ParentTable and ChildTable tables but not about the entity classes themselves, My code should not still compile and fail on run. I need to use some code that depends on the relation so that if something is wrong with the relation this code fails early and I know about it the problem the next time I build the VS project.

The Final Solution

I tried badly to get the entity name from the API, after frustration, I ended up writing this code:


<span class="cb1">using</span> System;

<span class="cb1">using</span> System<span class="cb2">.</span>Collections<span class="cb2">.</span>Generic;

<span class="cb1">using</span> System<span class="cb2">.</span>Data<span class="cb2">.</span>Objects;

<span class="cb1">using</span> System<span class="cb2">.</span>Data<span class="cb2">.</span>Objects<span class="cb2">.</span>DataClasses;

<span class="cb1">using</span> System<span class="cb2">.</span>Linq<span class="cb2">.</span>Expressions;

<span class="cb1">using</span> System<span class="cb2">.</span>Reflection;


<span class="cb1">namespace</span> Meligy<span class="cb2">.</span>Samples<span class="cb2">.</span>EntityFramework

{

    <span class="cb1">public</span> <span class="cb1">static</span> <span class="cb1">class</span> <span class="cb3">LinqExenstions</span>

    {

        <span class="cb4">//Used for child entities.</span>

        <span class="cb4">//Example: Order.Customer</span>

        <span class="cb1">public</span> <span class="cb1">static</span> <span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> Include<span class="cb2"><</span>T<span class="cb2">></span>(<span class="cb1">this</span> <span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> parent,

                                                <span class="cb3">Expression</span><span class="cb2"><</span><span class="cb5">Func</span><span class="cb2"><</span>T, <span class="cb3">StructuralObject</span><span class="cb2">>></span> expression)

            <span class="cb1">where</span> T : <span class="cb3">StructuralObject</span>

        {

            <span class="cb1">return</span> Include(parent, (<span class="cb3">LambdaExpression</span>) expression);

        }


        <span class="cb4">//Used for child collections of entities.</span>

        <span class="cb4">//Example: Order.OrderLines</span>

        <span class="cb1">public</span> <span class="cb1">static</span> <span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> Include<span class="cb2"><</span>T<span class="cb2">></span>(<span class="cb1">this</span> <span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> parent,

                                                <span class="cb3">Expression</span><span class="cb2"><</span><span class="cb5">Func</span><span class="cb2"><</span>T, <span class="cb3">RelatedEnd</span><span class="cb2">>></span> expression)

            <span class="cb1">where</span> T : <span class="cb3">StructuralObject</span>

        {

            <span class="cb1">return</span> Include(parent, (<span class="cb3">LambdaExpression</span>) expression);

        }


        <span class="cb1">private</span> <span class="cb1">static</span> <span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> Include<span class="cb2"><</span>T<span class="cb2">></span>(<span class="cb3">ObjectQuery</span><span class="cb2"><</span>T<span class="cb2">></span> parent,

                                                 <span class="cb3">LambdaExpression</span> expression)

            <span class="cb1">where</span> T : <span class="cb3">StructuralObject</span>

        {

            <span class="cb4">//There must be only one root entity to load related entities to it.</span>

            <span class="cb1">if</span> (expression<span class="cb2">.</span>Parameters<span class="cb2">.</span>Count <span class="cb2">!=</span> <span class="cb6">1</span>)

            {

                <span class="cb1">throw</span> <span class="cb1">new</span> <span class="cb3">NotSupportedException</span>();

            }


            <span class="cb4">//We'll store entity names here in order then join them at the end.</span>

            <span class="cb1">var</span> entityNames <span class="cb2">=</span> <span class="cb1">new</span> <span class="cb3">List</span><span class="cb2"><</span><span class="cb1">string</span><span class="cb2">></span>();


            <span class="cb4">//We split the calls ... Entity.MemberOfTypeChild.ChildMemberOfChildMember etc..</span>

            <span class="cb4">//Example: (Order ord) => ord.Customer.Address</span>

            <span class="cb1">string</span>[] childTypesMembers <span class="cb2">=</span> expression<span class="cb2">.</span>Body<span class="cb2">.</span>ToString()<span class="cb2">.</span>Split(<span class="cb7">'.'</span>);


            <span class="cb4">//Get the root entity type to start searching for the types of the members inside it.</span>

            <span class="cb4">//In prev. example: Find: Order</span>

            <span class="cb3">Type</span> parentType <span class="cb2">=</span> expression<span class="cb2">.</span>Parameters[<span class="cb6">0</span>]<span class="cb2">.</span>Type;

            <span class="cb4">//entityNames.Add(GetEntityNameFromType(parentType));</span>


            <span class="cb4">//The first word in the expression is just a variable name of the root entity. </span>

            <span class="cb4">//  Skip it and start next.</span>

            <span class="cb4">//In example: First part is: ord</span>

            <span class="cb1">for</span> (<span class="cb1">int</span> i <span class="cb2">=</span> <span class="cb6">1</span>; i <span class="cb2"><</span> childTypesMembers<span class="cb2">.</span>Length; i<span class="cb2">++</span>)

            {

                <span class="cb1">string</span> memberName <span class="cb2">=</span> childTypesMembers[i];


                <span class="cb4">//Get the member from the root entity to get its entity type.</span>

                <span class="cb3">MemberInfo</span> member <span class="cb2">=</span> parentType<span class="cb2">.</span>GetMember(memberName)[<span class="cb6">0</span>];


                <span class="cb4">//We cannot get the type of the entity except by knowing</span>

                <span class="cb4">//  whether it's property or field (most likely will be property).</span>

                <span class="cb4">//Bad catch in the reflection API? Maybe!</span>

                <span class="cb3">Type</span> memberType <span class="cb2">=</span> member<span class="cb2">.</span>MemberType <span class="cb2">==</span> <span class="cb8">MemberTypes</span><span class="cb2">.</span>Property

                                      <span class="cb2">?</span> ((<span class="cb3">PropertyInfo</span>) member)<span class="cb2">.</span>PropertyType

                                      : ((<span class="cb3">FieldInfo</span>) member)<span class="cb2">.</span>FieldType;


                <span class="cb4">//Add the eneity name got from entity type to the list.</span>

                entityNames<span class="cb2">.</span>Add(GetEntityNameFromType(memberType));


                <span class="cb4">//The next member is belong to the child entity, so,</span>

                <span class="cb4">//  the root entity to seach for members should be the child entity type.</span>

                parentType <span class="cb2">=</span> memberType;

            }


            <span class="cb4">//Join the entity names by "." again.</span>

            <span class="cb1">string</span> includes <span class="cb2">=</span> <span class="cb1">string</span><span class="cb2">.</span>Join(<span class="cb7">"."</span>, entityNames<span class="cb2">.</span>ToArray());


            <span class="cb4">//Simulate the original Include(string) call.</span>

            <span class="cb1">return</span> parent<span class="cb2">.</span>Include(includes);

        }


        <span class="cb1">private</span> <span class="cb1">static</span> <span class="cb1">string</span> GetEntityNameFromType(<span class="cb3">Type</span> type)

        {

            <span class="cb4">// We didn't just use the Entity type names because maybe</span>

            <span class="cb4">//  the table is called Orders and the class is Order or OrderEntity.</span>


            <span class="cb1">if</span> (type<span class="cb2">.</span>HasElementType) <span class="cb4">//For arrays, like: OrderLines[]</span>

            {

                <span class="cb4">//The type of the element of the array is what we want.</span>

                type <span class="cb2">=</span> type<span class="cb2">.</span>GetElementType();

            }

            <span class="cb1">else</span> <span class="cb1">if</span> (type<span class="cb2">.</span>IsGenericType) <span class="cb4">// for collections, like: EntityCollection<OrderLines></span>

            {

                <span class="cb1">var</span> genericClassTypeParameters <span class="cb2">=</span> type<span class="cb2">.</span>GetGenericArguments();


                <span class="cb4">//The generic class must have one entity type only to load it.</span>

                <span class="cb1">if</span> (genericClassTypeParameters<span class="cb2">.</span>Length <span class="cb2">!=</span> <span class="cb6">1</span>)

                    <span class="cb1">throw</span> <span class="cb1">new</span> <span class="cb3">NotSupportedException</span>();


                <span class="cb4">//The type of the element of the collection is what we want.</span>

                type <span class="cb2">=</span> genericClassTypeParameters[<span class="cb6">0</span>];

            }


            <span class="cb4">//Get the attributes that have the entity name in them.</span>

            <span class="cb1">var</span> entityTypeAttributes <span class="cb2">=</span>

                type<span class="cb2">.</span>GetCustomAttributes(<span class="cb1">typeof</span> (<span class="cb3">EdmEntityTypeAttribute</span>), <span class="cb1">true</span>) <span class="cb1">as</span> <span class="cb3">EdmEntityTypeAttribute</span>[];


            <span class="cb4">//Make sure there IS one and ONLY one attribute to get the only entity name.</span>

            <span class="cb1">if</span> (entityTypeAttributes <span class="cb2">==</span> <span class="cb1">null</span> <span class="cb2">||</span> entityTypeAttributes<span class="cb2">.</span>Length <span class="cb2">!=</span> <span class="cb6">1</span>)

                <span class="cb1">throw</span> <span class="cb1">new</span> <span class="cb3">NotSupportedException</span>();


            <span class="cb4">//Return the entity name.</span>

            <span class="cb1">return</span> entityTypeAttributes[<span class="cb6">0</span>]<span class="cb2">.</span>Name;

        }

    }

}

This enables you to write:

DbDataContext.Categories.Include( (cat)=> cat.Prodycts);

Or:

DbDataContext.Prodycts.Include( (prod)=> prod.Category);

According to your need.

For things like: Order.Customer.Address (multiple levels), you’ll have to write code like:

DbDataContext.Orders.Include( order => order.Customer ).Include( customer => Customer.Address );

What’s Eager Loading? (in case you don’t know)

Let’s say you have tables Products, and Categories with relation 1<->* between them (Any category has many products; one product has one category). Let’s say you want to display a page of all products grouped by categories. Something like the following list but with much more information of course:

    • Category 1
      • Product A
      • Product B
      • Product C
    • Category 2
      • Product X
      • Product Y
      • Product Z

If you are using some ORM / Code generator that creates for you classes like “Product”, “Category” and gives you properties like “myCategory.Products” , “myProduct.Category”, how would you create such page?

Normally you’ll put a repeater or such for products inside a repeater for categories.

image

The products repeater will have its data source set to the current category item of the Categories repeater, something like “( (CategoryEntity)Container.DataItem ).Products”. Fine with that? Familiar?

OK. Now, if the code generator that generated the “Products” property has something like that:

public List<PRoduct> _Products;

public List<PRoduct> Products

{

get

{

if (_Products == null)

{

_Products = (from products in DB.Products

where products.CategoryID == this.ID

select products)

.ToList();

}

return _Products;

}

}

  • Nevermind the LINQ syntax. It’s just like writing “SELECT * FROM [Products] WHERE …” with all the dataset/datareader stuff.

Lazy Loading (AKA. Deferred Loading)

If the generated code (or your code) looks like this, this means that that for every category in the database, you’ll have a separate DB call to get the products of this category.

It also means that the products of each category will not be loaded until someone writes code that calls the getter of the Products property. That’s why this style of coding (not loading the related objects until they’re asked to be loaded) is called Lazy Loading.

This is good for a single category where you may be seeking just the basic information of the category and will not try to load products, since then they will not be requested when you don’t ask for it.

However, this is very bad for our example page. Because it means that a new DB call for each category. Imagine that you have 20 or 50 or 100 Category there, this will give you how many DB calls? (Hint: N Categories + 1 call for the category list itself).

Eager Loading

What if the code in the getter above was in the constructor?. Imaging something like:

public Category(int categoryID)

{

// Code that laods category info from DB. Does not matter here.

//Could be in factory method or such. Not our topic

_CategoryID = categoryID;

// …. …. …. Continue Loading Logic

//The important part

_Products = (from products in DB.Products

where products.CategoryID == this.ID

select products)

.ToList();

}

This is good in case you know that in every situation when you use the category, the Products will be needed. This is probably not useful in a Product/category Scenario but think of a Person table and Address table where most likely whenever you load a Person you’re going to load his Addresses.

This is also useful especially when using ORM/code generator as in the first example. Lets get back to the Repeater example. If you use Entity framework or similar ORM, and you set the Categories query to load the Products eager loading (meaning each Category is created with its Products loaded already), Entity Framework can have a single connection and only TWO database hits, one for the Categories, and one for the Products. This is very useful in many listing scenarios. It also help especially when you have many parent objects (say Categories) or if the parent object needs to load entities of many different classes (say User needs to load Roles and Permissions and Personal Information and History and …. (if such case is applicable for you of course.

Now that you know what eager loading is, you can go up and check how the Entity Framework does that.

Share With Friends:

How did I learn that?

As a bonus for coming here, I'm giving away a free newsletter for web developers that you can sign up for from here.

It's not an anything-and-everything link list. It's thoughtfully collected picks of articles and tools, that focus on Angular 2+, ASP.NET (4.x/MVC5 and Core), and other fullstack developer goodies.

Take it for a test ride, and you may unsubscribe any time.

You might also want to support me by checking these out [Thanks]: