.net - 奇怪的 LINQ to SQL .Union() 错误

标签 .net linq-to-sql

(完整复制请参见底部)

使用以下实体...

[Table]
internal sealed class Employee
{
    private EntityRef<Employee> manager;

    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    private int Id;

    [Column]
    private int? ManagerId;

    [Column]
    internal bool IsOverpaid;

    [Association(Name = "Manager_Subordinate", Storage = "manager", ThisKey = "ManagerId", IsForeignKey = true)]
    internal Employee Manager
    {
        get { return this.manager.Entity; }
        set { this.manager.Entity = value; }
    }
}

...此查询失败并出现 NotSupportedException,并显示消息“Union 或 Concat 中的类型构造不兼容。”:
var overpaidTopManagers =
    from employee in context.Employees
    where employee.IsOverpaid && (employee.Manager == null)
    select employee;
var managersWithOverpaidSubordinates =
    from employee in context.Employees
    where employee.IsOverpaid && (employee.Manager != null)
    select employee.Manager;
var query = overpaidTopManagers.Union(managersWithOverpaidSubordinates);

我真的不明白为什么,两个查询都会产生相同类型的实体,所以合并它们应该不是问题吗?

完整复制如下:
using System;
using System.Data.Linq;
using System.Data.Linq.Mapping;
using System.Linq;

internal static class Program
{
    private static void Main(string[] args)
    {
        using (var context = new Context("Whatever.sdf"))
        {
            if (!context.DatabaseExists())
            {
                context.CreateDatabase();
            }

            var overpaidTopManagers =
                from employee in context.Employees
                where employee.IsOverpaid && (employee.Manager == null)
                select employee;
            var managersWithOverpaidSubordinates =
                from employee in context.Employees
                where employee.IsOverpaid && (employee.Manager != null)
                select employee.Manager;
            var query = overpaidTopManagers.Union(managersWithOverpaidSubordinates);

            // This throws a NotSupportedException with the Message
            // "Types in Union or Concat are constructed incompatibly."
            foreach (var manager in query)
            {
                Console.WriteLine(manager.ToString());
            }
        }
    }
}

[Table]
internal sealed class Employee
{
    private EntityRef<Employee> manager;

    [Column(IsPrimaryKey = true, IsDbGenerated = true)]
    private int Id;

    [Column]
    private int? ManagerId;

    [Column]
    internal bool IsOverpaid;

    [Association(Name = "Manager_Subordinate", Storage = "manager", ThisKey = "ManagerId", IsForeignKey = true)]
    internal Employee Manager
    {
        get { return this.manager.Entity; }
        set { this.manager.Entity = value; }
    }
}

internal sealed class Context : DataContext
{
    internal Table<Employee> Employees;

    internal Context(string fileOrServerOrConnection) : base(fileOrServerOrConnection)
    {
        this.Employees = this.GetTable<Employee>();
    }
}

最佳答案

问题与允许外键列为空的事实有关。尝试将 ManagerId 列更改为不允许 null(只需添加一个指向自身的“nobody”占位符值以表示层次结构的根)并再次尝试联合,它现在应该可以工作了。不过不要问我为什么,还在挖掘 Linq2Sql 源代码...

更新(初步答案,从我的脑海中浮出水面):
正如我所怀疑的,这个异常(exception)与 ManagerId 的事实有关。可以为空。异常文本具有误导性:错误不是因为两个查询结果的类型不兼容,而是因为左右查询的内部表示类型不兼容。当 Linq2Sql 发现 FK(即 ManagerId)可以为空时,它采用不同的代码路径。您看到的查询中隐藏了一个联接 ( employee.Manager ) 和 if ManagerIdInt32 类型然后 Linq2Sql 知道它可以执行内部联接。如果 ManagerId但是,它是一个可为空的 int,然后 Linq2Sql 发现它需要进行左连接,即使在提供的示例中,由于 filter 子句,它可以使用内部连接。

解决该问题的一种方法是在执行联合之前具体化一个或两个有问题的查询(即调用 .ToList() 或另一种合适的扩展方法)。

关于.net - 奇怪的 LINQ to SQL .Union() 错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6835649/

相关文章:

linq-to-sql - 存储库应该向服务层公开 IQueryable 还是在实现中执行过滤?

c# - 如何找出当前的溢出检查上下文?

c# - 如何更改网格中所有文本框的字体大小,windows app uwp

c# - ASP.net MVC - 每个 View 或每个操作一个 ViewModel?

sql - 使用 REST API 的 Linq to SQL?

c# - 我如何使用 LINQ 来汇总此数据——按这些关系的计数分组的关系数

c# - 警告说 Linq-To-Sql 表达式总是错误的,但这是不正确的,为什么?

c# - System.MissingMethodException Message=未找到方法 : DryIoc. 规则 DryIoc.Rules.WithoutFastExpressionCompiler()

c# - 关于使用 Nginx 将 Web 请求代理到基于 .Net HttpListener 的 Json 服务的想法

c# - LINQ to SQL 查询以确定值是否以数字开头