我尝试使用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,但是要联合Type1
,Type2
等。我必须将它们转换为BaseType
,这在LINQ-to-SQL中是不允许的
在同一个查询中以任何方式执行此操作?
答案 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()
,你可以随意使用