Entity Framework - Selective Condition on Included Navigation Property

Assume I have these simplified EF generated entities...

public class PurchaseOrder
{
     public int POID {get;set;}
     public int OrderID {get;set;}
     public int VendorID {get;set;}
     public IEnumerable<Order> Orders {get;set;}
}

public class Order
{
     public int OrderID {get;set;}
     public decimal Price {get;set;}
     public IEnumerable<Item> Items {get;set;}
}

public class Item
{
     public int OrderID {get; set;}
     public string SKU {get;set;}
     public int VendorID {get;set;}
     public Order Order {get;set;}
}

Business Logic:

An order can have multiple POs, one for each distinct vendor on the order (vendors are determined at the Item level).

How Can I selectively Include Child Entities?

When querying for POs, I want to automatically include child entites for Order and Item.

I accomplish this, using Include()...

Context.PurchaseOrders.Include("Orders.Items");

This does it's job and pulls back related entities, but, I only want to include Item entities whose VendorID matches the VendorID of the PurchaseOrder entity.

With traditional SQL, I'd just include that in the JOIN condition, but EF builds those internally.

What LINQ magic can I use tell EF to apply the condition, without manually creating the JOINs between the entities?

Answers


You can't selectively pull back certain child entities that match a certain condition. The best you can do is manually filter out the relevant orders yourself.

public class PurchaseOrder
{
     public int POID {get;set;}
     public int OrderID {get;set;}
     public int VendorID {get;set;}
     public IEnumerable<Order> Orders {get;set;}

     public IEnumerable<Order> MatchingOrders {
         get {
            return this.Orders.Where(o => o.VendorId == this.VendorId);
         }
     }
}

You can't. EF doesn't allow conditions for eager loading. You must either use multiple queries like:

var pos = from p in context.PurchaseOrders.Include("Order")
          where ...
          select p;
var items = from i in context.Items
            join o in context.Orders on new { i.OrderId, i.VendorId} 
               equals new { o.OrderId, o.PurchaseOrder.VendorId }
            where // same condition for PurchaseOrders
            select i;

Or you can use projection in single query:

var data = from o in context.Orders
           where ...
           select new
              {
                  Order = o,
                  PurchaseOrder = o.PurchaseOrder,
                  Items = o.Items.Where(i => i.VendorId == o.PurchaseOrder.VendorId)
              };

You could use the IQueryable-Extensions here:

https://github.com/thiscode/DynamicSelectExtensions

The Extension builds dynamically an anonymous type. This will be used for projection as described by @Ladislav-Mrnka.

Then you can do this:

var query = query.SelectIncluding( new List<Expression<Func<T,object>>>>(){

//Example how to retrieve only the newest history entry
x => x.HistoryEntries.OrderByDescending(x => x.Timestamp).Take(1),

//Example how to order related entities
x => x.OtherEntities.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing),

//Example how to retrieve entities one level deeper
x => x.CollectionWithRelations.Select(x => x.EntityCollectionOnSecondLevel),

//Of course you can order or subquery the deeper level
//Here you should use SelectMany, to flatten the query
x => x.CollectionWithRelations.SelectMany(x => x.EntityCollectionOnSecondLevel.OrderBy(y => y.Something).ThenBy(y => y.SomeOtherThing)),

});

Need Your Help

How to check the length value of cell in datagrid view?

vb.net winforms

I'm able to validate the cells if they are empty but I'm not able to check the length of the cell. I want the user to enter 5 digits and if it is less than 5, show up a message box.

About UNIX Resources Network

Original, collect and organize Developers related documents, information and materials, contains jQuery, Html, CSS, MySQL, .NET, ASP.NET, SQL, objective-c, iPhone, Ruby on Rails, C, SQL Server, Ruby, Arrays, Regex, ASP.NET MVC, WPF, XML, Ajax, DataBase, and so on.