如何基于输入参数数组动态构建LINQ查询

时间:2020-07-20 14:46:03

标签: linq

我有下一个LINQ查询,它模拟了两个数据表上的完全外部联接

{"r1(0)", "r2(0)", "r1(1)", "r2(1)", "math.abs(cdec(r1(2))-cdec(r1(3)))", "math.abs(cdec(r2(2))-cdec(r2(3)))"}

adn返回Object [] []。

我的愿望是将{"math.abs(cdec(r1(2))-cdec(r1(3)))", "math.abs(cdec(r2(2))-cdec(r2(3)))"}传递到LINQ查询模式的WHERE子句中。

在传递(From r1 As datarow In dt1 From r2 As datarow In dt2 Where math.abs(CDec(r1(2))-CDec(r1(3))).Equals( math.abs(CDec(r2(2))-CDec(r2(3))) ) Select r1.ItemArray.Concat(r2.itemarray).ToArray).toarray 的情况下,LINQ查询应如下所示:

"r1(0), r2(0); r1(1), r2(1); math.abs(cdec(r1(2))-cdec(r1(3))), math.abs(cdec(r2(2))-cdec(r2(3)))"

我可以选择传递字符串dt1.AsEnumerable.AsQueryable .Join( dt2.AsEnumerable, "new(get_Item(0) as Column0, get_Item(1) as Column1)", "new(get_Item(0) as Column0, get_Item(1) as Column1)", "new(outer.get_Item(4) as Column4, inner.get_Item(4) as Column4)" )

非常感谢您的帮助。

巨大更新

尝试使用此表达式(基于System.Linq.Dynamic.Core)

in_dt1.AsEnumerable.AsQueryable
.Join(
      in_dt2.AsEnumerable,
      "new(math.abs(cdec(get_Item(2) as Column2)-cdec(get_Item(3) as Column3)) as A)",
      "new(math.abs(cdec(get_Item(2) as Column2)-cdec(get_Item(3) as Column3)) as A)",
      "new(outer.get_Item(4) as Column4, inner.get_Item(4) as Column4)"
)

抛出“发现模糊匹配”。

也将我的表情从以前的改变为

{{1}}

引发“')'或','预期”异常。

1 个答案:

答案 0 :(得分:0)

如果您纠正了类型错误并使用了正确的功能,则可以使其正常工作:

var ans = ((IQueryable)dt1.AsEnumerable().AsQueryable())
             .Join(dt2.AsEnumerable(),
                   "new(get_Item(0) as c0, get_Item(1) as c1, math.abs(Convert.ToDecimal(get_Item(2))-Convert.ToDecimal(get_Item(3))) as c2)",
                   "new(get_Item(0) as c0, get_Item(1) as c1, math.abs(Convert.ToDecimal(get_Item(2))-Convert.ToDecimal(get_Item(3))) as c2)",
                   "new(outer.get_Item(4) as Column0, inner.get_Item(4) as Column1)"
               );

动态LINQ不支持创建数组,但是使用Reflection可以将对象转换为数组-但是,我不确定我会推荐这样做。

使用以下扩展方法将任何对象转换为提供的类型(必须可转换)的数组:

public static class TypeExt {
    public static List<MemberInfo> GetPropertiesOrFields(this Type t, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
        t.GetMembers(bf).Where(mi => mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property).ToList();
}

public static class MemberInfoExt {
    public static object GetValue(this MemberInfo member, object srcObject) {
        switch (member) {
            case FieldInfo mfi:
                return mfi.GetValue(srcObject);
            case PropertyInfo mpi:
                return mpi.GetValue(srcObject);
            case MethodInfo mi:
                return mi.Invoke(srcObject, null);
            default:
                throw new ArgumentException("MemberInfo must be of type FieldInfo, PropertyInfo or MethodInfo", nameof(member));
        }
    }
}

public static class ObjectExt {
    public static T[] ToArray<T>(this object obj, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) {
        List<T> ans;
        if (obj is IDictionary<string, object> id)
            ans = id.Values.Select(v => (T)v).ToList();
        else {
            ans = new List<T>();
            foreach (var prop in obj.GetType().GetPropertiesOrFields(bf)) {
                try {
                    ans.Add((T)prop.GetValue(obj));
                }
                catch (Exception) {
                    // ignore inaccessible properties
                }
            }
        }
        return ans.ToArray();
    }
}

您可以将创建的匿名对象转换为string[]

var ans2 = ans.AsEnumerable()
              .Select(v => ((object)v).ToArray<string>())
              .ToList();