【问题标题】:C# Linq - get objects with not empty collectionC# Linq - 获取非空集合的对象
【发布时间】:2015-08-09 02:28:05
【问题描述】:

我有:

private Dictionary<int, Сolor[]> colorSet = new Dictionary<int, Сolor[]>()
{
   {1, new Сolor[2] {Сolor.Red, Сolor.Green}},
   {2, new Сolor[2] {Сolor.Yellow, Сolor.Blue}},
   ...
};

public class Graph
{
   public Сolor Сolor { get; set; }
   public ICollection<Point> Points { get; set; }
}

1) 我如何从Points 不为空的数据库中获取List&lt;Graph&gt;

List<Graph> graphs = context.Graphs.Where(g => g.Points.Count > 0).ToList()

2) 如何执行?

List<Graph> graphs = context.Graphs.Where(g => colorSet[1].Contains(g.Color)).ToList()

例外是:

LINQ to Entities 无法识别方法 '...' 方法,并且 此方法无法转换为商店表达式。

【问题讨论】:

  • 使用g.Points.Any() 将产生比g.Points.Count &gt; 0 更有效的查询。它让 SQL 生成 if Exists(select 1 from ...) 查询而不是 if (select count(*) from ...) &gt; 0 查询
  • colorSet[1].Contains(g.Color) 绝对不会转换为存储表达式或 sql,因为您将 linq to object 与 linq to sql 混合在一起。尝试在 where 之前调用 ToList。
  • @JenishRabadiya 在 where 之前执行 .ToList 会导致他的整个数据库被加载到客户端上。通常不是一个好主意。
  • @ScottChamberlain 没错,但它不适用于 linq to object 和 linq to sql 的混合。或者他需要为此创建存储过程。
  • Color 类是什么?是你自己的枚举、枚举还是实体?

标签: c# linq


【解决方案1】:

.Contains 不能在数据库上下文的 .Where 子句中使用。您可以使用 .Any 或 .All 进行比较。

List<Graph> graphs = context.Graphs.Where(g => colorSet[1].Any(c => c.Color == g.Color)).ToList()

Using contains() in LINQ to SQL

【讨论】:

  • 错误:'Color[]'不包含'Any'的定义和最佳扩展方法重载'System.Linq.Enumerable.Any(System.Collections.Generic.IEnumerable, System.Func)' 有一些无效参数
  • 刚刚更新,你需要一个 .Any 和 .All 方法中的 lambda 表达式(或返回 bool 的东西)
  • 实际上,Contains 可以 被使用并产生一个 `field IN (X,Y,Z)` 子句 - 就像“我一直使用它英孚”。这是导致问题的colorSet[1]。链接指向String.Contains,而不是IEnumerable&lt;T&gt;.Contains
  • @PanagiotisKanavos 你是对的。 linq to sql 和 linq to object 的混合无论如何都不起作用。
  • 看来,一步无法选择数据。谢谢。
【解决方案2】:

在 EF 中使用Enumerable.Contains() 没有问题。无法翻译的是String.Contains

第二个查询的问题是您将 LINQ 运算符与对象访问代码混合在一起,特别是 colorSet[1]。 LINQ to EF 不会尝试执行此代码,也不知道如何将其转换为 SQL。

解决办法是先把colorSet的值存入变量:

var colors = colorSet[1];
List<Graph> graphs = context.Graphs
                            .Where(g => colors.Contains(g.Color))
                             .ToList();

LINQ to EF 知道将 Enumerable&lt;T&gt;.Contains() 转换为 AND Color IN (0,1) 子句

这假设您至少使用 EF 5,Color 是一个枚举并且指向一个自定义类,例如:

public enum Color
{
    Red,
    Green,
    Blue,
    Yellow
}
public class Point
{
    public int Id { get; set; }
    public int X { get; set; }
    public int Y { get; set; }
}

更新

至于第一个问题,你可以使用Any()检索有任意点的图:

var graphs=context.Graphs
                  .Where(g => g.Points.Any())
                  .ToList();

Linq to EF 将生成带有WHERE EXISTS 子句的 SQL 语句,例如

WHERE  EXISTS (SELECT 
    1 AS [C1]
    FROM [dbo].[Points] AS [Extent2]
    WHERE [Extent1].[Id] = [Extent2].[Graph_Id]
)

这将返回具有点但不包含点本身的图形。当您尝试访问 Graph 对象的 Points 属性时,它们将以惰性方式加载。如果您只想访问几个 Points 属性,这可能会提高性能,但如果您想访问所有属性,则会导致大量查询(N+1 问题)

如果你也想加载Points,你需要使用Include()方法,例如:

var graphs=context.Graphs
                  .Include(g => g.Points)
                  .Where(g => g.Points.Any())
                  .ToList();

这将在图形和点之间执行左连接,并在单个查询中返回所有数据。

【讨论】:

  • 不是绝对必要的。选择以大表达式执行。但是谢谢。
猜你喜欢
  • 1970-01-01
  • 2018-08-04
  • 1970-01-01
  • 2018-11-03
  • 1970-01-01
  • 2017-03-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多