【问题标题】:How to make a join expression dynamically in linq?如何在 linq 中动态创建连接表达式?
【发布时间】:2016-09-08 11:10:24
【问题描述】:
(listProductx, listProductx2) => listProductx.ProductName == listProductx2.ProductName && listProductx.ProductCode == listProductx2.ProductCode

在上面的示例中,我想动态设置 ProductName 和 ProductCode。这些是列名;我们可以将其存储在数组或任何地方。我想从数组或列表中动态加入它。

【问题讨论】:

  • 您是说要在动态列上加入 listProductx 和 listProductx2 吗?
  • @sr28 yes... 有没有办法创建动态表达式?
  • 如果您仍然遇到问题,请展示您的 listProductx 类并用文字说明您想要匹配的内容。
  • 您是否打算加入内存枚举(列表、数组等)或数据库表(例如,EF 可查询)?

标签: c# linq


【解决方案1】:

假设您的目标是 LINQ to Objects。

实际上Join 方法需要3 个表达式(函数)- 外键选择器、内键选择器和结果选择器。由于您要连接两个相同类型的可枚举,因此外部和内部键选择器将是相同的。

如果属性的数量不超过7,我们可以使用Tuple<>作为key。以下是我们如何动态构建选择器:

static Func<T, object> CreateSelector<T>(IEnumerable<string> propertyNames)
{
    var sourceType = typeof(T);
    var parameter = Expression.Parameter(sourceType, "e");
    var properties = propertyNames.Select(name => Expression.PropertyOrField(parameter, name)).ToArray();
    var selector = Expression.Lambda<Func<T, object>>(
        Expression.Call(typeof(Tuple), "Create", properties.Select(p => p.Type).ToArray(), properties),
        parameter);
    return selector.Compile();
}

然后我们可以创建一个使用它的辅助方法(将这两个方法放在您选择的某个顶级公共静态类中):

public static IEnumerable<Tuple<T, T>> Join<T>(this IEnumerable<T> left, IEnumerable<T> right, IEnumerable<string> propertyNames)
{
    var keySelector = CreateSelector<T>(propertyNames);
    return left.Join(right, keySelector, keySelector, Tuple.Create);
}

现在你可以使用这样的东西了:

class Product
{
    public string ProductName { get; set; }
    public string ProductCode { get; set; }
}

List<Product> list1 = ...;
List<Product> list2 = ...;
var result = list1.Join(list2, new [] { "ProductName", "ProductCode" });

【讨论】:

  • 我可以使用上面的result 作为Func&lt;T1, T2, bool&gt; compareValue 的参数吗?
  • 我的函数是public static bool CompareTwoLists&lt;T1, T2&gt;(IEnumerable&lt;T1&gt; list1, IEnumerable&lt;T2&gt; list2, Func&lt;T1, T2, bool&gt; compareValue) { return list1.Select(item1 =&gt; list2.Any(item2 =&gt; compareValue(item1, item2))).All(search =&gt; search) &amp;&amp; list2.Select(item2 =&gt; list1.Any(item1 =&gt; compareValue(item1, item2))).All(search =&gt; search); }
  • 我正在调用这个函数var JoinExp = listProduct.Join(listProduct2, new[] { "ProductName", "ProductCode" });bool IsSuccess2 = ListComparison.CompareTwoLists(listProduct, listProduct2, (listProductx, listProductx2) =&gt; Convert.ToBoolean(JoinExp)),但我得到了错误。无法转换类型为“d__37`4 的对象
  • 谢谢...它现在工作正常。我修改了调用函数JoinExp.Any()
  • 你能改变连接表达式两个 列表吗?像 。因为我需要从两个具有相同属性的不同列表中加入
【解决方案2】:

如果我理解正确,您想动态构建比较表达式。您可以根据 https://msdn.microsoft.com/en-us/library/mt654267.aspx 和 SO 问题 How do I dynamically create an Expression<Func<MyClass, bool>> predicate from Expression<Func<MyClass, string>>? 的文档来执行此操作。大体的做法是一样的,只需要构建不同的表达式树即可。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    • 1970-01-01
    • 2014-07-06
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    相关资源
    最近更新 更多