有趣的是,在阅读了您的帖子并进行了一些测试后,我意识到您的实际操作对我来说似乎工作得很好,因为您在两个查询中显示为省略号的投影部分都匹配。您会看到,LINQ to SQL 似乎是根据属性赋值语句构造 SQL 选择命令的基础投影,而不是物化的实际类型,只要双方具有相同的编号、类型和顺序(不确定关于这个)的成员分配 UNION 查询应该是有效的。
我一直在使用的解决方案是在我的 DataContext 类上创建一个属性,该属性的作用很像 SQL 视图,因为它允许我编写查询(在我的情况下是两个不同表之间的联合),然后在编写只读选择语句时,将该查询当作一个表来使用。
public partial class MyDataContext
{
public IQueryable<MyView> MyView
{
get
{
var query1 =
from a in TableA
let default_ColumnFromB = (string)null
select new MyView()
{
ColumnFromA = a.ColumnFromA,
ColumnFromB = default_ColumnFromB,
ColumnSharedByAAndB = a.ColumnSharedByAAndB,
};
var query2 =
from a in TableB
let default_ColumnFromA = (decimal?)null
select new MyView()
{
ColumnFromA = default_ColumnFromA,
ColumnFromB = b.ColumnFromB,
ColumnSharedByAAndB = b.ColumnSharedByAAndB,
};
return query1.Union(query2);
}
}
}
public class MyView
{
public decimal? ColumnFromA { get; set; }
public string ColumnFromB { get; set; }
public int ColumnSharedByAAndB { get; set; }
}
注意两个关键点:
首先,由构成联合两半的查询形成的投影具有相同的列数、类型和顺序。现在 LINQ 可能要求顺序相同(对此不确定),但 SQL 确实适用于 UNION,我们可以确定 LINQ 至少需要相同类型和数量的列以及这些“列”成员 assignments 知道,而不是您在投影中实例化的类型的属性。
其次,LINQ 目前不允许在用于制定 Concat 或 Union 的查询的投影中使用多个常量,据我了解,这主要是因为这两个单独的查询在处理 Union 操作之前分别进行了优化。通常 LINQ to SQL 足够聪明,可以意识到如果您有一个仅在投影中使用的常量值,那么为什么将它发送到 SQL 只是为了让它以原来的方式返回,而不是将其作为帖子添加原始数据从 SQL Server 返回后处理。不幸的是,这里的问题是,这是一个 LINQ to SQL 智能的例子,因为它在这个过程中过早地优化了每个单独的查询。我发现解决这个问题的方法是使用 let 关键字为投影中的每个值形成一个范围变量,这将通过从常量中获取它的值来实现。不知何故,这会欺骗 LINQ to SQL 将这些常量传递给实际的 SQL 命令,该命令将所有预期的列保留在结果 UNION 中。更多关于这项技术的信息可以在here找到。
使用这种技术,我至少有一些可重用的东西,这样无论实际的 Union 可以变得多么复杂或丑陋,尤其是使用范围变量,在您的最终查询中,您都可以将查询写入这些伪视图,例如 MyView 和 deal与下面的复杂性。