简体   繁体   中英

EF Core Filtered Include: “Lambda expression used inside Include is not valid”

I am trying to use a very basic function using the new "Filtered Include" functionality with EF Core 5.0.0,

https://docs.microsoft.com/en-us/ef/core/querying/related-data

but I am running into an issue. It gives the following error:

System.InvalidOperationException: Lambda expression used inside Include is not valid.

Here is the code snippet that is causing it. According to the documentation, I believe that my logic is correct. I am not sure as to why this is happening. Is there something I am missing?

List<Vendor> vendors = context.Vendors
                .Include(v => v.Items.Where( i => i.Rating > 1)
                .ToList();



Vendors is the parent of items. It is a very basic setup. One (vendor) to many (item) relationship.

It is usually more efficient to use Select instead of Include. Using Select you can select the properties that you plan to use. Include will transfer the complete item, inclusive the properties you won't use.

Consider a School with Students in a straightforward one-to-many relation: Every School has zero or more Students, every Student attends exactly one School, namely the School that the foreign key SchoolId refers to.

Suppose you want to Query School [10] with its 2000 Students. Every Student of School [10] will have a value for foreign key SchoolId equal to 10. If you use Include you will Transfer this value about 2000 times, while you already know the value. What a waste of processing power.

When querying data, always use Select and select only the properties you actually plan to use. Only use Include if you plan to modify the included data.

var result = context.Vendors.Select(vendor => new
{
     // Select only the Vendor properties that you intend to use:
     Id = vendor.Id,
     Name = vendor.Name,
     ...

     HighRatedItems = vendor.Items.Where(item => item.Rating > 1)
         .Select(item => new
         {
              // again: select only the properties that you plan to use
              Id = item.Id,
              ...

              // not needed, you know the value:
              // VendorId = item.VendorId,
         })
         .ToList(),
})
.ToList();

I use automatic types here. If you really want Vendors:

var result = context.Vendors.Select(vendor => new Vendor
{
     // Select only the Vendor properties that you intend to use:
     Id = vendor.Id,
     Name = vendor.Name,
     ...

     Items = vendor.Items.Where(item => item.Rating > 1)
         .Select(item => new Item
         {
              // again only the items that you plan to use.
              Id = item.Id,
              ...            
         })
         .ToList(),
});

Be aware, that some of the properties will not have a proper value. This might be confusing for users of your method. But then again: they know that they won't get "Vendors with their Items" anyway, because you don't return all their items, so they are already not expecting full Vendor objects.

To overcome the problem of not fully filled returned objects, consider implementing a repository pattern. separate the types of the tables in your database from the actually returned data.

This will need extra classes that seem very much the same as your original Vendors and Items. Yet it has the advantage that users will know exactly what they get. However the benefits are that your users don't have to change the code if you decide to change the layout of your database. Furthermore the repository hides that you are using a database. It could be a CSV file, or Json, or maybe even just a Dictionary. This makes it much easier to unit test your users.

Whether you should implement a repository depends on how many users you will have for your database, how important unit testing is, and how often you expect your database to change.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM