LINQ to SQL 或任何其他启用了 LINQ 的 ORM 是不是支持许多关联过滤?

Posted

技术标签:

【中文标题】LINQ to SQL 或任何其他启用了 LINQ 的 ORM 是不是支持许多关联过滤?【英文标题】:Does LINQ to SQL or any other LINQ enabled ORM support many association filtering?LINQ to SQL 或任何其他启用了 LINQ 的 ORM 是否支持许多关联过滤? 【发布时间】:2011-07-20 16:42:14 【问题描述】:

显然,根据this,您无法在不加载整个集合的情况下查询多个关系。换句话说,它是 LINQ to Objects 而不是 LINQ to Entities 查询。

例如

Category category = db.Categories.Find(1);
var productsThatStartWithA = category.Products.Where(p => p.Name.StartsWith("A")).ToList();

上述查询加载该类别中的所有产品,然后应用过滤器。

问题 #1

这是正确的吗?我不敢相信这是多么愚蠢。

问题 #2

LINQ to SQL 或任何其他 LINQ 启用 ORM 是否按其应有的工作方式处理此问题?


linked question 和Slauma's 答案中提供的替代方案是解决方法,而不是解决方案。它们需要对上下文对象的引用,如果您编写的代码与实体框架 API 分离,并且/或者您的代码是面向对象的,则该引用不可用,例如:

public class Category  

   public IEnumerable<Product> GetProductsThatStartWithA() 
      return this.Products.Where(p => p.Name.StartsWith("A")).ToList()
   

【问题讨论】:

我不明白。链接问题中的答案为您提供了两种方式来加载过滤的导航集合,不是吗?您的示例是 LINQ to Objects,对(假设您对 Products 属性进行了延迟加载,那么它是完整集合的 DB 查询,然后是加载的集合上的 LINQ to Objects。如果您没有延迟加载您的代码将无论如何都会崩溃。)但这只是LINQ to Objects,因为您没有使用答案中的一种方法。 您提议的代码混合了实体服务和实体的关注点。这里没有上下文是有充分理由的。你可能会发现Mark Seeman's post on application boundaries informative. @Craig Stuntz:这只是我试图实现的一个示例,以及我认为使用 ORM 应该能够实现的目标。讨论设计模式不是这个问题的重点。 【参考方案1】:

可悲的是,Linq to SQL 实际上为此提供了更好的支持(使用DataLoadOptions.AssociateWith,但它仍然不是您想要的),而 Linq to entity / EF 仅提供您所谓的解决方法——即使关于IEnumerableIQueryable 之间的实现和现有差异看起来是正确的解决方案。

您想要的将需要集合公开IQueryable 并在内部调用@Slauma 显示的代码,如果集合未加载 = linq-to-entities 或加载集合上的常见 linq-to-objects。我不确定它是否可以工作,但你可以玩它。这里有一些起点:

Extra-Lazy Collection Count with EF 4.1 (Part 1) Extra-Lazy Collection Count with EF 4.1 (Part 2)

顺便说一句。你必须关闭 EF 的延迟加载,否则它会优先。

【讨论】:

【参考方案2】:

像您的示例中的查询这样的查询只能在内存中(LINQ to Objects)是不正确的。

您可以通过两次往返数据库来实现对集合的过滤加载 - 请参阅 Ladislav 对链接问题的回答中的选项 1。要将其转换为您的具体示例,它将如下所示:

Category category = db.Categories.Find(1);
var productsThatStartWithA = db.Entry(category)
    .Collection(c => c.Products)
    .Query()
    .Where(p => p.Name.StartsWith("A"))
    .ToList();

这不会加载完整的Products 集合,而只会加载过滤后的集合。它是 LINQ to Entities 而不是 LINQ to Objects,它可以在有和没有延迟加载的情况下工作。

此外,通过投影到匿名类型,您可以通过单次往返和单次查询获得相同的结果:

var result = db.Categories.Where(c => c.Id == 1)
    .Select(c => new
    
        Category = c,
        ProductsStartingWithA = c.Products.Where(p => p.Name.StartsWith("A"))
    )
    .SingleOrDefault();

在这里,您可以在result.Categoryresult.ProductsStartingWithA 中找到您的类别和过滤产品集合。

【讨论】:

这些是变通方法,而不是解决方案。这两种替代方法都需要对上下文对象的引用,如果您编写的代码与实体框架 API 分离,则该引用不可用。 @Max Toro:没有动态上下文就不可能发出数据库查询(有或没有过滤)。那应该如何工作?上下文是数据库的唯一网关。您必须明确地(如在我的示例中)或隐式/隐藏在代理对象中以进行延迟加载的上下文。在这两种情况下,都必须引用尚未处理的 EF 上下文实例。如果您处于断开连接的情况,则没有希望从数据库中查询某些内容。这不适用于任何 ORM。 查看添加到问题中的示例。 @Max Toro:在您编辑之前,我并不清楚您不想明确引用上下文。 Products 必须是某种代理集合,它在内部引用上下文以使您的方法栩栩如生。 (不能是简单的List&lt;T&gt;HashSet&lt;T&gt; 之类的。)我只能告诉你,EF 代理不支持在数据库上运行过滤查询。使用 EF,您必须使用上述解决方案之一。其他 ORM 可能具有此功能,但我不知道。 但我怀疑是否有解决方案,只要你想在你的模型中拥有普通的集合接口/类——比如ICollection&lt;T&gt;。无论您使用什么 ORM,您都只会拥有那些集合类型的方法。他们的 LINQ 扩展方法始终是 IEnumerable&lt;T&gt; 的方法,这意味着 LTO。如果 ORM 支持您正在寻找的内容,则必须将模型中的集合类型更改为该 ORM 的某些自定义类型(IQueryableCollectionOfORMXYZ&lt;T&gt;),或者 - 在最好的情况下 - 也许是 IQueryable&lt;T&gt;。但是,这超出了我的视野范围内的细节。【参考方案3】:

您需要记住 LINQ to Entities 和 LINQ to Objects 之间的区别。

LINQ to Entities 中的.Where() 将转换为 SQL。 LINQ to Objects 中的 .Where 不会。

您可以非常轻松地通过使用 LINQ to Entities 使用 SQL 来限制实体列表。您还可以非常轻松地通过使用 LINQ to Objects 避免默默地和不可见地生成 DB 查询。

简而言之,之所以存在差异,正是因为,正如您所暗示的,有时(但不是每次都使用 SQL)是件好事。如果这对您来说似乎“愚蠢”,那么我会温和地建议您花一些时间来了解您期望事情如何工作并了解它们实际上是如何工作的。如果您的期望与实施一致,它确实有意义。

【讨论】:

如果你真的这样做(坦率地说,你对这个答案的否决让我怀疑你是否只是在发泄),那么我同意@Slauma。 Slauma 和您都没有回答这个问题,这就是投反对票的原因。 你似乎没有真正的问题;您断言 LINQ to Entities(和 LINQ to SQL,出于同样的原因)通过以它们的方式使用 LINQ API 正在做一些“错误”的事情。我强烈不同意这一点。 我认为微软了解如何使用 LINQ。 非查询对象应该使用 L2O;您坚持认为他们不应该,尽管有多个相反的例子,实施您的建议会损害常见场景的性能,以及有 简单 方法来做你想做的事你似乎不想使用。 我不知道public virtual ICollection&lt;Product&gt; Products get; 的对象类型,但应该是查询对象。无论哪种方式,我都可以接受,如果有替代品(其他 ORM),那是我的第二个(真正的)问题。 不,EntityCollections 不是查询。它们是收藏品。 L2S 也是如此。

以上是关于LINQ to SQL 或任何其他启用了 LINQ 的 ORM 是不是支持许多关联过滤?的主要内容,如果未能解决你的问题,请参考以下文章

LINQ To SQL 能否用于其他数据库(MySQl、Oracle 等)

通配符搜索LINQ to SQL

什么是Linq to SQL相当于TOP或LIMIT / OFFSET?

具有多个包含的Linq-to-Sql

我应该开始使用 LINQ To SQL 吗?

LINQ to SQL Designer和Geography数据类型