Entity Framework 带约束的延迟加载

Entity Framework Lazy Loading With contraint

我想知道是否可以通过延迟加载来包含约束。我要加载的对象有一个 属性 'isDeleted'。我只想加载未删除的结果。所以,例如。

allCustomers = db.customers.include("documents").toList

我希望 return 所有客户和任何 isDeleted = false 的文档。我知道我可以在 return 集上限定它,但我什至不想从数据库中取回它们。有什么办法吗?

如果只想查询值,最好不要使用 Include(string)

显然客户和他的文档之间存在 one-to-many 关系:每个 Customer 有零个或多个 Documents,每个 Document 正好属于一个 [=14] =].

(也可以是many-to-many关系,但原理是一样的。)

因此,如果您根据 entity framework code first conventions, 设计了 ​​classes,您将得到类似于:

class Customer
{
    public int Id {get; set;}

    // every Customer has zero or more Documents:
    public virtual ICollection<Document> Documents {get; set;}

    ... // other properties
}
class Document
{
    public int Id {get; set;}

    // every Document belongs to exactly one Customer using foreign key:
    public int CustomerId {get; set;}
    public virtual Customer Customer {get; set;}

    ... // other properties
}

这就是 entity framework 检测到您在 Customers 和他们的 Documents 之间设计了 one-to-many 关系所需知道的全部内容。可能是您的 class 名称或您的属性具有不同的标识符。您可能已经使用属性或流利 API 解决了这个问题,但原理保持不变。

仅当您计划更新获取的项目时才使用包含

数据库查询中速度较慢的部分之一是将数据从 DBMS 传输到您的进程。因此,明智的做法是将数据限制为您真正打算使用的数据。

如果您要求 non-deleted 客户提供他们所有的文件,您正在传输比需要更多的数据:

  • 每个客户都有一个字段 IsDeleted。您已经知道这些值将为 false
  • 每个客户都有一个主键 ID。他的 100 个文档中的每一个都有一个外键 CustomerId,其值与 Customer.Id 相同。查询他们真是浪费!

因此,除非您打算更新查询值,否则使用 Select 而不是 Include 总是更明智。这样您就可以调整查询以仅获取您实际计划在您的用例中使用的数据。

回到你的问题

您想获得所有 non-deleted 客户的(全部或部分)他们的 non-deleted 文件。

var result = myDbContext.Customers
    // I only want the non-deleted Customers:
    .Where(customer => !customer.IsDeleted)
    .Select(customer => new
    {
        // select only the properties you plan to use:
        Id = customer.Id,
        Name = customer.Name,
        ...

        Documents = customer.Documents
            // I only want the non-deleted Documents:
            .Where(document => !document.IsDeleted) 
            .Select(document => new
            {
                // again select only the properties you plan to use:
                Title = document.Title,
                Date = document.PublicationDate,
                Version = document.Version,
                ...

                // not needed:
                // CustomerId = document.CustomerId,
            })
            .ToList(),
    });

因为 entity framework 了解您的 one-to-many 关系,它知道主键和外键并将为您执行正确的 Join。

只有当您确实需要更新所有 non-deleted 客户的所有文档时,才明智地使用 Include。我的建议是不要使用带字符串参数的版本,而要使用带 属性 表达式的版本。参见 IQueryable Include。该版本是类型安全的。如果您使用 non-existing 属性,您的编译器会报错:

var itemsToUpdate = myDbContext.Customers
    .Include(customer.Documents)
    .Where(customer => !customer.IsDeleted);

但恕我直言,您很少希望一次更新所有客户的所有文档。通常您会更新一个文档或一个客户。

因此,如果您需要更新 one-to-one 关系,您通常需要包含:客户及其唯一的 HomeAddress

var customerToUpdate = myDbContext.Customers
   .Include(customer => customer.HomeAddress)
   .Where(customer => customer.Id == customerId)
   .SingleOrDefault();

customer.HomeAddress.Street = "Downing street",
customer.HomeAddress.Number = 10,
customer.HomeAddress.City = "London",
myDbContext.SaveChanges();