Использование system.linq.dynami c для объединения таблиц - PullRequest
0 голосов
/ 05 февраля 2020

Есть ли способ использовать system.linq.dynami c для объединения таблиц, используя строку для имени таблицы?

Кажется, что это будет основа c, но все точки гуглинга для некоторых чрезвычайно сложные ответы, расширяющие функциональность linq, и мне кажется, что я просто упускаю что-то простое.

Мое приложение позволяет людям создавать фильтры на основе данных, динамически добавляя критерии и поля из нескольких таблиц.

У меня есть ArrayList полей в формате «TableName.FieldName» в виде строк. Динамический запрос c должен быть создан с использованием этого списка строк.

У меня есть основная таблица Table1 в качестве базовой отправной точки. Поэтому при построении запроса они всегда начинаются там, но могут добавлять поля из других таблиц после. если включена какая-либо из других таблиц, она должна выполнить соединение с таблицей 1. Использование первичного ключа.

Использование system.linq.dynami c Создание предложения 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;
                    }
                }
            }

1 Ответ

1 голос
/ 06 февраля 2020

Вы можете использовать Reflection для извлечения таблиц из 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 чтобы отобразить строки в таблицы или использовать одну из высокоскоростных библиотек свойств Reflection.

Создать карту с чем-то вроде:

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