使用2个列的通用LINQ JOIN扩展方法

3

我创建了一个通用的扩展方法,可以基于一个共同的列连接2个表格。代码如下:

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});
        }
    }

现在数据库中的表有两个共同列(Id,Type),我需要编写一个通用的扩展方法来基于这两个共同列连接这些表,如下所示:

    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.

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

有没有指针可以指导我在哪里出错了?

编辑:

现在我有一个编译成功的方法,但我遇到了一个运行时错误:LINQ表达式节点类型“Lambda”不受实体LINQ支持。

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
@jdweng..我已经尝试了你说的。虽然没有编译器错误,但是出现了运行时错误“LINQ表达式节点类型'Lambda'在LINQ to Entities中不受支持。”我正在努力弄清楚。谢谢。 - Anurag
@jdweng:请查看编辑并提供一些指针。谢谢。 - Anurag
1个回答

3
你已经完成了所有的工作:不需要使用JoinExtensionTwoColumns方法,你可以使用JoinExtension来基于多个列连接表格:
tableA.JoinExtension<TypeA, TypeB, object>(tableB, x => new { x.column1, x.column2 }, 
    x => new { column1 = x.column2, column2 = x.column3  });

但在这种情况下,解决方案不是类型安全的,因为第三个任意的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...谢谢你的回答。让我试试你的建议。 - Anurag
@Slava...你对JoinExtensionTwoColumns的编辑会生成一个运行时错误,错误信息为“LINQ表达式节点类型'Invoke'在LINQ to Entities中不受支持”。你有什么想法吗?同时我正在尝试你的第一种方法。 - Anurag
这个错误发生是因为你可能在EF上下文中执行了此查询。当然,这种情况会发生,但你没有在问题中指明这一事实。如果你尝试在简单的List<T>集合上使用这种方法,一切都会正常工作。 - Slava Utesinov
你应该首先在问题上指定 ef 标签。你是在编译时还是运行时遇到了这个错误,并且要指定所有列的类型。 - Slava Utesinov
@Slava,请查看编辑。 - Anurag
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接