【问题标题】:Using system.linq.dynamic to join tables使用 system.linq.dynamic 连接表
【发布时间】:2020-02-04 21:43:41
【问题描述】:

有没有办法使用 system.linq.dynamic 使用字符串作为表名来连接表?

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

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

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

我有一个名为 Table1 的主表作为基本起点。因此,在构建查询时,他们总是从那里开始,但之后可以从其他表中添加字段。如果包含任何其他表,则它必须使用主键连接回 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()));

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

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

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

【问题讨论】:

  • 你能用Where()创建一个伪连接吗?

标签: entity-framework linq linq-to-sql linq-to-entities


【解决方案1】:

您可以使用反射从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 }
                };

【讨论】:

  • 这看起来正是我需要的,太棒了!会试试这个并报告!非常感谢!
  • @Kris 我还添加了手动创建的Dictionary 可能性。
  • 因此,如果我将字符串用于连接参数,这对我不起作用我收到错误“严重代码描述项目文件行抑制状态错误 CS0411 方法'Queryable.Join(IQueryable, IEnumerable, Expression>, Expression>, Expression>)'无法从用法中推断出来。请尝试显式指定类型参数。
  • @Kris 也许更新您的问题或使用新代码和问题提出新问题。
  • 所以@NetMage 你可以使用字符串然后执行连接吗?还是您假设 linq.dynamic 会允许这样做?问题是一样的,我们如何从包含“Table.Field”的字符串列表中构建具有多个表和字段的动态查询
猜你喜欢
  • 2023-04-07
  • 1970-01-01
  • 2017-04-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多