Entity Framework Include with Func

Update: I made a new version here.

In Northwind DB, if you want to load categories with products, you will use Include method:

context.Categories.Include(“Products”)

But what I really find bad is the fact that Products is a string. When we use LINQ, we can’t understand it.

So I define an extension method:

public static class ObjectQueryExtension

{

    public static ObjectQuery<T> Include<T>(this ObjectQuery<T> mainQuery, Expression<Func<T, object>> subSelector)

    {

        return mainQuery.Include(((subSelector.Body as MemberExpression).Member as Reflection.PropertyInfo).Name);

    }

}


and I can now doing this:

context.Categories.Include(c => c.Products)


which is I think really better. Isn’t it?


 

This entry was posted in 7671, 7674. Bookmark the permalink.

21 Responses to Entity Framework Include with Func

  1. Niraj says:

    Can I do something like this:

    from patient in datacontext.PATIENTS.Include(pat => pat.PATIENTCONTACTS.Where(c => c.ISACTIVE == “T”))

    It’s throwing a null reference expection? Any idea. Thanks.

  2. It’s normal. The Include doesn’t allow this case.

    It should be interesting to change the ExpressionTree to do this but it isn’t the goal of my method.

    In your case, you can do something like this:

    var q = from patient in datacontext.PATIENTS

            select new { Patient = patient, PatientActiveContacts = patient.PATIENTCONTACTS.Where(c => c.ISACTIVE == “T”) };

    else, if you always want only the active contacts, it should be interesting to specify it directly in your EDM as I showed in my EDM article.

  3. Niraj says:

    I can’t use anonymous types. I am working a disconnected mode (EF over WCF To Presentation). I very much need the patients with only active contacts. Any other workarounds? Thanks in advance.

  4. The problem is to load on the context only PATIENTCONTACTS you want. 

    So, there is probably a better way to do this but you can do this:

    var patients = datacontext.PATIENTS;

    foreach (var patient in patients)

        context.PATIENTCONTACTS.Where(p => p.PATIENT.PATIENTID == patient.PATIENTID && p.ISACTIVE == “T”).ToList();

  5. Else, of course, you can create a type with Patient and PatientActiveContacts properties and use the first query without anonymous type.

  6. Daniel Simmons propose what is probably the best way for you:

    Patient AttachContacts(Patient patient, IEnumerable<PatientContact> contacts)

    {

        patient.PATIENTCONTACTS.Attach(contacts);

        return patient;

    }

     

    var q = from patient in datacontext.PATIENTS

            select new { Patient = patient, PatientActiveContacts = patient.PATIENTCONTACTS.Where(c => c.ISACTIVE == “T”) };

    var fullQuery = from anonType in q.AsEnumerable()

                    select AttachContacts(anonType.Patient, anonType.PatientActiveContacts);

  7. Nilotpal says:

    I can’t see the include() method in my entity class… Why I wonder… my email is nilotpal.das@microsoft.com would you be able to help…?

  8. Matthieu MEZIL says:

    First, Include is an extension method.
    So you need to add the using of the ObjectQueryExtension in your file which will use the Include method.
    Moreover, Include is not an extension method for entity class but for ObjectQuery.

  9. Robert says:

    How would you do a nested includes, something like getting the manufacturer from a product.
    Where I would just write .Include(“Products”).Include(“Products.Manufacturer”);

    context.Categories.Include(c => c.Products).Include(c.Products.Any(p => p.Manufacturer);

    How would you do that?

  10. Jared Wein says:

    Hi Matthieu,

    Can you explain how this static class and it’s static method are interpreted in to the framework to allow this behavior?

    I don’t see any use of the partial or override keyword here.

  11. Matthieu MEZIL says:

    My Include method is an extension method (keyword this before the first parameter).
    Moreover, when you do: c => c.Products (which is a lambda expression), you have an Expression and so I can treat it.

  12. eXcess says:

    It does use Reflection though, but that won’t cause a performance drop I suppose?

  13. Matthieu MEZIL says:

    @eXcess : I also think so but I did no test. I just want to remind you that it’s a POC.
    Martin should try it in a production application this week. I am very interested to know if it is usable in a real world application.

  14. Hi, I’m trying to convert the extension method above into VB. But I get an exception. Here’s my VB translation:

    Module Module1

    Function Include(Of T)(ByVal mainQuery As ObjectQuery(Of T), ByVal subSelector As Expressions.Expression(Of Func(Of T, Object))) As ObjectQuery(Of T)

    Return mainQuery.Include(CType(CType(subSelector.Body, MemberExpression).Member, Reflection.PropertyInfo).Name)
    End Function

    End Module

    The exception I get is that an UnaryExpression cannot be converted into MemberExpression. Any suggestions?

    Thank you!

  15. Matthieu MEZIL says:

    Hi Alessandro,

    In VB, you must change it like this:

    Module ObjectQueryExtension

        <Extension()> _

        Function Include(Of T)(ByVal mainQuery As ObjectQuery(Of T), ByVal subSelector As Expression (Of Func (Of T, Object)))

            Return mainQuery.Include(CType(CType(CType(subSelector.Body, UnaryExpression).Operand, MemberExpression).Member, PropertyInfo).Name)

        End Function

    End Module

  16. DonSleza4e says:

    Hi
    What about including several tables?

    for example, load shops and categories with products

  17. Matthieu MEZIL says:

    You can do :

    context.Products.Include(p => p.Category).Include(p => p.OrderDetails);

    If you want more level, you should look at it.

  18. DonSleza4e says:

    Thanks, thats I looking for!
    Amazing!

  19. Nelson says:

    This is really much better, but I’m trying to do something like:

    this.Context.Countries.Include(c => c.MetroAreas.OrderBy(m => m.Name)).Include(c => c.Regions.OrderBy(r => r.Name));

    And its coming up with:

    + Error {System.Windows.Ria.Data.EntityOperationException: Object reference not set to an instance of an object.
    at System.Windows.Ria.Data.HttpDomainClient.GetRequestResult(HttpDomainClientAsyncResult httpAsyncResult)
    at System.Windows.Ria.Data.HttpDomainClient.EndQueryCore(IAsyncResult asyncResult)
    at System.Windows.Ria.Data.DomainClient.EndQuery(IAsyncResult asyncResult)
    at System.Windows.Ria.Data.DomainContext.CompleteLoad(IAsyncResult asyncResult)} System.Exception {System.Windows.Ria.Data.EntityOperationException}

    Any ideas?

  20. Matthieu MEZIL says:

    I think it’s very hard to do it. You should look at this.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>