Entity Framework - Выборочное условие для включенного свойства навигации - PullRequest
6 голосов
/ 01 декабря 2011

Предположим, у меня есть эти упрощенные EF-объекты ...

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;}
}

Бизнес-логика:

В заказе может быть несколько заказов на поставкупо одному для каждого отдельного поставщика в заказе (поставщики определяются на уровне позиции).

Как можно выборочно включать дочерние объекты?

При запросах на PO,Я хочу автоматически включать дочерние объекты для Order и Item.

Я выполняю это, используя Include () ...

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

Это выполняет свою работу и откатывает связанные объекты, но, Я только хочу включить сущности Item, VendorID которых соответствует VendorID сущности PurchaseOrder .

В традиционном SQL я просто включил бы это в условие JOIN,но EF создает их внутренне.

Какую магию LINQ я могу использовать, чтобы сказать EF применить условие, не создавая вручную СОЕДИНЕНИЯ между сущностями?

Ответы [ 3 ]

3 голосов
/ 01 декабря 2011

Вы не можете.EF не допускает условий для быстрой загрузки.Вы должны использовать несколько запросов, таких как:

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;

Или вы можете использовать проекцию в одном запросе:

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)
              };
3 голосов
/ 01 декабря 2011

Вы не можете выборочно возвращать определенные дочерние объекты, которые соответствуют определенному условию.Лучшее, что вы можете сделать, это вручную отфильтровать соответствующие заказы.

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);
         }
     }
}
1 голос
/ 04 июля 2013

Вы можете использовать IQueryable-Extensions здесь:

https://github.com/thiscode/DynamicSelectExtensions

Расширение динамически создает анонимный тип.Это будет использоваться для проецирования, как описано @ Ladislav-Mrnka.

Тогда вы можете сделать это:

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)),

});
...