使用 system.linq.dynamic 连接表

Using system.linq.dynamic to join tables

有没有一种方法可以使用 system.linq.dynamic 加入 table 并使用 table 名称的字符串?

这似乎是基本的,但所有谷歌搜索都指向一些扩展 linq 功能的极其复杂的答案,这似乎我只是遗漏了一些简单的东西。

我的应用程序允许人们通过从多个 table 中动态添加条件和字段来构建数据过滤器。

我有一个字段的 ArrayList,格式为 "TableName.FieldName" 作为字符串。 必须使用此字符串列表创建动态查询。

我有一个名为 Table1 的主要 table 作为基本起点。因此,在构建查询时,他们总是从那里开始,但可以在之后添加其他 table 的字段。如果包含任何其他 table,则它必须使用主键连接回 Table1。

使用 system.linq.dynamic 构建 where 子句似乎非常容易。

var query = dbcontext.Table1;
Dictionary<string, ArrayList> reportTables; //store table and its respective field names as an array
query.Select(string.Join(",", reportTables["Table1"].ToArray()));

但是现在我怎样才能轻松加入子tables呢?

我开始循环遍历 tables,如果它不是主要的 table,我想添加这样的连接:

  if(reportTables.Keys.Count > 1){
                    // add joins
                    foreach(string tblName in reportTables.Keys)
                    {
                        if(tblName != "Table1")
                        {
                            query.Join(tblName, "Table1.IDField", tblName + ".Table1IDField")
                        }
                    }
                }

更新:


感谢@NetMage,我能够通过

让应用程序编译和工作
var query = (IQueryable)db.Table1;

Dictionary<string, IQueryable> tableTypeDictIQ = new Dictionary<string, IQueryable>()
                {
                    { "Table2", db.Table2},
                    { "Table3", db.Table3 }
}

if (reportTables.Keys.Count > 1)
            {
                // add joins
                var joinCount = 0;
                foreach (string tblName in reportTables.Keys)
                {
                    if (tblName != "Table1")
                    {
                        if (joinCount == 0)
                            query = query.Join(tableTypeDictIQ[tblName], "RECDNO", "RECDNO", "new(outer as Table1,inner as Table2)");
                        else
                            query = query.Join(tableTypeDictIQ[tblName], "Table1.RECDNO", tblName + ".RECDNO", $"new({string.Join(",", Enumerable.Range(1, joinCount + 1).Select(n => $"outer.Table{n} as Table{n}"))}, inner as Table{joinCount + 2})");
                        ++joinCount;
                    }
                }
            }

您可以使用反射从 DataContext 中检索表。

定义了这些扩展方法:

public static class ObjectExt {
    public static object GetValue(this object obj, string memberName) =>
        obj.GetType().GetPropertyOrField(memberName).GetValue(obj);

    public static TRes GetValue<TRes>(this object obj, string memberName) =>
        obj.GetType().GetPropertyOrField(memberName).GetValue<TRes>(obj);
}

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 T GetValue<T>(this MemberInfo member, object srcObject) => (T)member.GetValue(srcObject);
}

public static class TypeExt {
    public static MemberInfo GetPropertyOrField(this Type t, string memberName, BindingFlags bf = BindingFlags.Public | BindingFlags.Instance) =>
        t.GetMember(memberName, bf).Where(mi => mi.MemberType == MemberTypes.Field || mi.MemberType == MemberTypes.Property).Single();
}

你可以这样做:

var query = (IQueryable)dbcontext.Table1;
if (reportTables.Keys.Count > 1) {
    // add joins
    var joinCount = 0;
    foreach (string tblName in reportTables.Keys) {
        if (tblName != "Table1") {
            if (joinCount == 0)
                query = query.Join(dbcontext.GetValue<IQueryable>(tblName.Dump("Join tblName")), "IDField", "Table1IDField", "new(outer as Table1,inner as Table2)");
            else
                query = query.Join(dbcontext.GetValue<IQueryable>(tblName), "Table1.IDField", tblName + ".Table1IDField", $"new({String.Join(",", Enumerable.Range(1, joinCount + 1).Select(n => $"outer.Table{n} as Table{n}"))}, inner as Table{joinCount+2})".Dump("Select"));
            ++joinCount;
        }
    }
}

var ans = query.Select(("new("+string.Join(",", reportTables.Keys.SelectMany(t => reportTables[t].Cast<string>().Select(f => $"{t}.{f}")).ToArray().Dump("fields"))+")").Dump("Select"));

完成后,这将创建一个包含所有连接表的扁平化匿名对象 new { T1, T2, T3, ... },以供进一步查询。

如果性能是一个考虑因素,除了使用我的扩展方法之外,您还可以创建一个 Dictionary 来将字符串映射到表,或者使用其中一个高速反射 属性 库。

用类似的东西创建地图:

var tableMap = new Dictionary<string, IQueryable>() {
                    { "Table2", Table2 },
                    { "Table3", Table3 }
                };