【问题标题】:How to union two LINQ queries but the second query need have more fields如何联合两个 LINQ 查询,但第二个查询需要更多字段
【发布时间】:2015-04-30 06:13:51
【问题描述】:

我需要在两个 LINQ 查询之间建立联合,但第二个查询需要比第一个查询更多的字段。我该怎么做?

例子:

public static void Dummy()
{
    var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });

    var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });

    var result = query1.Union(query2);
}

当我调用 result.ToList() 时,出现以下错误:

类型'MyObject'出现在两个结构上不兼容 单个 LINQ to Entities 查询中的初始化。一个类型可以是 在同一个查询中的两个地方初始化,但前提是相同 两个地方都设置了属性,并且这些属性设置在 相同的顺序。

我该如何解决这个问题?

Obs.:我无法将Field3 放入query1 中(我无权访问查询,因为我无法更改它)

【问题讨论】:

  • 错误信息不言自明!如果您已经在 SQL Server 等中使用过Union,那么它真的很容易理解它想要表达的意思。

标签: c# linq entity-framework


【解决方案1】:

您不必将Field3 放在第一个查询中,但Union 需要相同数量的列和相同的顺序。为第三列/字段C 指定一个虚拟值,例如:

var query1 = this.Db.Table1.Select(s => new MyObject() 
                        { A = s.Field1, B = s.Field2 , C= ""});

C 指定为Field3 的默认值,对于引用类型可能是null,对于数字等可能是0

如果您无权访问它,请修改 query1,然后使用 query1 创建一个新查询,例如:

var newQuery = query1.Select(s=> new MyObject() 
                            { A = A, B = B , C= ""}); 

然后在Union中使用它

var result = newQuery.Union(query2);

【讨论】:

  • 我无法更改查询1,因为我无权访问查询。
  • @RenatoLeite,你可以在query1上创建一个新查询,我已经添加了一些新代码,试试看。
  • 我尝试了,但没有解决问题,错误仍然存​​在:(
【解决方案2】:

照原样,你不能。您只能合并 2 个具有相同结构的集合。如果你不介意修改query1,但是:

var query1 = this.Db.Table1.Select(s => new MyObject() 
                                        { A = s.Field1, B = s.Field2, C = null });

这将允许它们正确地联合,因为它们具有相同的结构。

【讨论】:

    【解决方案3】:

    你可以这样做:

    创建一个从 MyObject 派生的对象

    class MyObjectUnion : MyObject{
    }
    

    所以,方法是这样的:

    public static void Dummy()
    {
        var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
    
        var query1modified = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = null });
    
        var query2 = this.Db.Table2.Select(s => new MyObjectUnion() { A = s.Field1, B = s.Field2, C = s.Field3 });
    
        var result = query1modified.Union(query2);
    }
    

    有效

    【讨论】:

      【解决方案4】:

      因为 query1 中的记录永远不会具有属性“C”,而 query2 中的所有记录都将具有属性“C”,所以 query1 中的记录不太可能等同于 query2 中的记录。使用 Union 而不是 Concat 的唯一原因是删除重复项,由于您没有任何重复项,因此您应该使用 Concat 而不是 Union。

      public static void Dummy()
      {
          var query1 = this.Db.Table1.Select(s => new MyObject() { A = s.Field1, B = s.Field2 });
      
          var query2 = this.Db.Table2.Select(s => new MyObject() { A = s.Field1, B = s.Field2, C = s.Field3 });
      
          var result = query1.ToList().Concat(query2);
      }
      

      有一些例外,就像您有一个忽略“C”属性的 MyObject 的自定义 IEqualityComparer,或者“C”属性的默认值可能存在于 table2 的记录中,并且您想要删除重复项,或者如果 query1 或 query2 中可能存在重复项并且您希望将它们删除,那么您仍然可以使用 Concat,但您需要在 Concat 之前使用 Distinct。

      已编辑以强制在通过 .ToList() 连接之前实现 query1

      使用 LinqPad 进行了双重检查,以下可执行文件没有问题,使用的数据源具有 Categories 和 Cities 表的模式完全不同:

      void Main()
      {
          var query1 = Categories.Select(s => new MyObject { A = s.id, B = s.name });
      
          var query2 = Cities.Select(s =>  new MyObject { A = s.id, B = s.city_name, C = s.location });
      
          var result = query1.ToList().Concat(query2);
          result.Dump();
      }
      public class MyObject
      {
          public int A {get;set;}
          public string B {get;set;}
          public object C {get;set;}
      }
      

      【讨论】:

      • @RenatoLeite 取决于您返回的数据的大小以及您是否希望必须通读所有记录。如果您不希望阅读所有记录(或多次阅读),请将 .ToList() 更改为 .AsEnumerable()
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多