在Union-Query中选择子集合

时间:2018-05-08 16:27:02

标签: entity-framework entity-framework-6

我尝试使用Entity Framework从数据库中选择对象为匿名类型。使用Union和选择子集时,我得到一个例外:

  

System.ArgumentException:' Distinct'操作不能应用于指定参数的集合ResultType。

我的模型包含从BaseType派生的几种类型。此基本类型引用RefType,其中包含ItemType的集合。从BaseType派生的类型存储在单独的表中,即Union。

查询如下所示:

var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items });
q1.Union(q2).ToList();

但是为了重现错误,您甚至可以联合相同类型的查询,只要您选择一个集合。

我会在union之后执行select,但是要联合Type1Type2等。我必须将它们转换为BaseType,这在LINQ-to-SQL中是不允许的

在同一个查询中以任何方式执行此操作?

2 个答案:

答案 0 :(得分:2)

ExpressionConverter尝试将表达式q1.Union(q2)转换为SQL时,实例框架的查询生成管道中会出现异常。

在有效查询中,您会看到EF为SQL查询添加了DISTINCT子句。具有集合属性(x.Ref.Items)的类型不会作为Distinct操作的有效参数传递,EF会抛出您看到的异常。

不幸的是,使用Concat代替Union并非有效的解决方法。 EF也会抛出异常:

  

不支持嵌套查询。 Operation1 =&#39; UnionAll&#39;操作2 =&#39; MultiStreamNest&#39;

这意味着它不支持连接包含具有集合属性的类型的嵌套查询。

所以你必须在内存中执行Union

var result = q1.AsEnumerable() // Continues the query in memory
               .Union(q2).ToList();

对于包含集合的匿名类型,C#没有任何问题:它只是将任何集合成员视为与另一个集合成员不相等。这确实意味着查询可以生成包含非唯一结果的集合(相同Id,相同Items),这在依赖Union隐式{时可能不会出现{1}}。

答案 1 :(得分:0)

我不确定为什么,由于某种原因,不同的是失败,可能因为它是匿名类型,并且它仍然是IQuerable,我建议触发这样的查询

var q1 = ctx.Set<Type1>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
var q2 = ctx.Set<Type2>().Select(x => new { x.Id, x.Ref.Items }).ToList<object>();
q1.Union(q2).ToList();

请注意,在这种情况下,Distinct将检查所有属性是否相等,这意味着如果2个对象具有相同的id但不同的项目,则两者都将存在。

如果您不关心不同的值,也可以使用concat

如果您关心不同且第一个选项不起作用,您可以使用group by并实现您自己的独特选项, 像这样的东西

var q1 = ctx.Set<Type1>().Select(x => new { Id = x.Id, Items =x.Ref.Items });
var q2 = ctx.Set<Type2>().Select(x => new { Id = x.Id, Items = x.Ref.Items });
//this will group by id, and select the first object items
var qFinal = q1.concat(q2).GroupBy(e => e.id)
.select(e => new {e.key, e.First().Items})
 .ToList();

也许你不想要First(),你可以随意使用