【问题标题】:Generic Extension method for LINQ JOIN using 2 columns使用 2 列的 LINQ JOIN 的通用扩展方法
【发布时间】:2016-10-23 20:22:40
【问题描述】:

我创建了一个通用的extension 方法来基于一个公共列加入 2 个tables。代码如下:

public class SomeDTO<T,U>
    {
        public T TableA { get; set; }
        public U TableB { get; set; }
    }
    public static class Helper
    {
        public static IQueryable<SomeDTO<T,U>> JoinExtension<T,U,Key>(this IQueryable<T> tableA, IQueryable<U> tableB, Expression<Func<T,Key>> columnA, Expression<Func<U,Key>> columnB)
        {
            return tableA.Join(tableB, columnA, columnB,(x, y) => new SomeDTO<T, U>{TableA = x,TableB = y});
        }
    }

现在database中的表有2个公共列(Id,Type),我需要编写一个公共扩展方法来基于2个公共列加入这些表,写了如下内容:

    public static IQueryable<SomeDTO<T, U>> JoinExtensionTwoColumns<T, U, Key>(this IQueryable<T> tableA, IQueryable<U> tableB, Expression<Func<T, Key>> columnA, Expression<Func<U, Key>> columnB, Expression<Func<T, Key>> columnC, Expression<Func<U, Key>> columnD)
   {
    return tableA.Join(tableB, a => new { columnA, columnB }, b => new { columnC, columnD }, (a, b) => new SomeDTO<T, U> { TableA = a, TableB = b });
   }

编译器在代码tableA.Join.... 行给我一个错误,如下所示:

The type arguments for method 'Queryable.Join<TOuter, TInner, TKey, TResult>(IQueryable<TOuter>, IEnumerable<TInner>, Expression<Func<TOuter, TKey>>, Expression<Func<TInner, TKey>>, Expression<Func<TOuter, TInner, TResult>>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.

它无法正确理解arguments 及其性质。

任何指向我可能出错的地方?

编辑:

我现在有一个编译成功的方法,但是我得到一个运行时错误 "The LINQ expression node type 'Lambda' is not supported in LINQ to Entities."

public static IQueryable<SomeDTO<T, U>> JoinExtensionTwoColumns<T, U, Key>(this IQueryable<T> tableA, IQueryable<U> tableB, Expression<Func<T, Key>> columnA, Expression<Func<U, Key>> columnB, Expression<Func<T, Key>> columnC, Expression<Func<U, Key>> columnD)
        {
return tableA.Join(tableB, a => new object[]{ columnA, columnB }, b => new object []{ columnC, columnD }, (a, b) => new SomeDTO<T, U> { TableA = a, TableB = b });
} 

这样调用方法:

var result= (db.table1.JoinExtensionTwoColumns<table1,table2,int>(db.table2, c => c.id.ToString(), d => d.id.ToString(),e => e.type, f => f.type)).Take(10);

还有更多指针。

【问题讨论】:

  • 您有一个匿名类型,它需要一个名称:new { columnA, columnB }(2 个位置)。您也可以使用 object 代替: new object[] { columnA, columnB } 。任何类型都会自动转换为对象,但在将类型对象分配给特定类型时需要进行转换。
  • @jdweng ..我已经尝试过你所说的。虽然没有编译器错误,但运行时错误“LINQ to Entities 不支持 LINQ 表达式节点类型 'Lambda'。”。我试图弄清楚。谢谢
  • @jdweng:请查看编辑并给出一些指示。谢谢

标签: c# entity-framework linq generics join


【解决方案1】:

您已经完成了所有工作:不需要JoinExtensionTwoColumns 方法,您可以使用JoinExtension 基于几个列连接表:

tableA.JoinExtension<TypeA, TypeB, object>(tableB, x => new { x.column1, x.column2 }, 
    x => new { column1 = x.column2, column2 = x.column3  });

但在这种情况下 - 解决方案 not TypeSafely 由于第三个 任意 - object 类型参数,要修复它,您可以通过这种方式重构以前的解决方案:

public static class Helper
{
    public class JoinCondition<TFirst, TSecond>
    {            
        public TFirst column1 { get; set; }
        public TSecond column2 { get; set; }
    }

    public static IQueryable<SomeDTO<T, U>> JoinExtension<T, U, TFirst, TSecond>(this IQueryable<T> tableA, IQueryable<U> tableB, 
        Expression<Func<T, JoinCondition<TFirst, TSecond>>> joinSelectorA, 
        Expression<Func<U, JoinCondition<TFirst, TSecond>>> joinSelectorB)
    {
        return tableA.Join(tableB, joinSelectorA, joinSelectorB, (x, y) => new SomeDTO<T, U> { TableA = x, TableB = y });
    }
}

实施:

var answer = context.TableA.JoinExtension(context.TableB, 
    x => new Helper.JoinCondition<int, string> { column1 = x.prop1, column2 = x.prop2}, 
    x => new Helper.JoinCondition<int, string> { column1 = x.prop3, column2 = x.prop4}
    ).ToList();

附: JoinExtensionTwoColumns 方法包含错误:你使用 columnA, columnB 等作为列,但它们是谓词,你可以 以这种方式修复它(它在 EF 上下文中不起作用):

public static IQueryable<SomeDTO<T, U>> JoinExtensionTwoColumns<T, U, Key>(this IQueryable<T> tableA, IQueryable<U> tableB, 
    Expression<Func<T, Key>> columnA, Expression<Func<U, Key>> columnB, 
    Expression<Func<T, Key>> columnC, Expression<Func<U, Key>> columnD)
{
    return tableA.Join(tableB, 
        a => new { column1 = columnA.Compile()(a), column2 = columnC.Compile()(a) }, 
        b => new { column1 = columnB.Compile()(b), column2 = columnD.Compile()(b) }, 
        (a, b) => new SomeDTO<T, U> { TableA = a, TableB = b });
}

【讨论】:

  • @Slava...谢谢你的回答。让我试试你的建议。
  • @Slava...您对 JoinExtensionTwoColumns 的编辑会生成运行时错误,尽管“LINQ to Entities 不支持 LINQ 表达式节点类型 'Invoke'。”。有什么想法吗?同时我正在尝试你的第一种方法。
  • 发生此错误,因为您可能在 EF 上下文中执行此查询。当然,它会发生,但你没有在你的问题上指定这个事实。如果您将在简单的List&lt;T&gt; 集合上尝试这种方法,那么一切都会奏效。
  • @Slava..我理解你..让我试试。
  • @Slava ..我必须使用 EF 上下文本身,在 db.Table1 和 db.Table2 上调用 JoinExtensionTwoColumns ..我无法将它们转换为列表然后执行。还有什么想法。此外,第一种方法失败“tableA.JoinExtension(tableB, x => new { x.column1, x.column2 }, x => new { column1 = x.column2, column2 = x.column3 });”,因为列是 int 和 string 类型,并且存在类型转换错误,因为我们使用的是匿名类型。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-09-09
相关资源
最近更新 更多