Как сделать дерево выражений «Join» с помощью Generic, а строка имеет внутреннее и внешнее выражение

ЗАЧЕМ МНЕ ЭТО НУЖНО:

Программа, над которой я работаю, имеет универсальный конструктор запросов, который использует выражение.trees для динамического запроса всех таблиц базы данных. С простым выбором и несколькими сотнями или тысячами данных он работает нормально. Когда пользователю нужны связанные данные таблицы, он использует ленивую загрузку и сопоставление для достижения того, что необходимо, но если (и когда) данные достигнут сотен тысяч, они будут непригодны для использования. Я пытаюсь удалить ленивую загрузку и сделать это с предварительным соединением в дереве выражений, что делает его одной поездкой.

Мне это нужно, потому что у пользователя есть возможность динамического поиска в БД, а наличие конкретных запросов для каждой таблицы очень сложно поддерживать, потому что у него много таблиц, которые нужно поддерживать вручную, а клиент требует много настроек.

ПРОБЛЕМА:

Чтобы выполнить следующий запрос,

IQueryable<A> As = db.A
                     .Join(
                           db.B,
                           _a => _a.bID,
                           _b => _b.ID,
                           (_a, _b) => new { a = _a, b = _b })
                     .Where(s => s.b.Name == "xpto")
                     .Select(s => s.a);

A получено имеет общий тип, а B получено имеет строку.

Использование блестящего @NetMage ?noredirect=1#comment113107421_63910903">ответ (это только часть. Полный ответ находится по ссылке или ниже):

private static Type TypeGenArg(this Expression e, int n) => e.Type.GetGenericArguments()[n];

  public static MethodCallExpression Join(this Expression outer, 
                                                    Expression inner, 
                                                    LambdaExpression outerKeyFne, 
                                                    LambdaExpression innerKeyFne, 
                                                    LambdaExpression resultFne) 
                                                    =>
                                                            Expression.Call(
                                                                TQueryable, 
                                                                "Join", 
                                                                new[] { outer.TypeGenArg(0), 
                                                                        inner.TypeGenArg(0), 
                                                                        outerKeyFne.ReturnType, 
                                                                        resultFne.ReturnType }, 
                                                                outer, 
                                                                inner, 
                                                                outerKeyFne, 
                                                                innerKeyFne, 
                                                                resultFne);

В следующем вызове, который делает MethodCallExpression 'join':

var join = db.A.AsConst().Join(db.B.AsConst(), aKeyFne, bKeyFne, resultFne);

Кажется, что db.A и db.B должны быть известны, но это не так, потому что клиент решает, какую таблицу просматривать, а «B» является следствием этого выбора.

В контексте этого класса; A - это общий тип T, а B - строка B. Таким образом, каждое преобразование, которое я пытался вызвать с помощью предыдущего оператора, вызывает различные типы ошибок, когда они получены в "объединении" или позже. Лучшая попытка была, когда я сделал:

        //GetType of Generic Type A
        AType = typeof(A);

        //GetType from string 'B'
        string BModelstr = "B";

        string areaM = "project.Models." + BModelstr;
        Assembly currentAssem = Assembly.GetExecutingAssembly();

        string assemblStr = currentAssem.ToString();


        string complete = areaM + "," + assemblStr;

        Type BType = Type.GetType(complete);

        //And then calling:

        var join1 = (db.Set(AType)).AsConst().Join((db.Set(BType)).AsConst(), aKeyFne, bKeyFne, resultFne);

Это вызвало ошибку на

private static Type TypeGenArg(this Expression e, int n) => e.Type.GetGenericArguments()[n];

Сказать, что это не дженерик.

ВОПРОС:

  • Как я могу использовать общий тип T как db.A и строку «B» как db.B, чтобы быть принятым в expression.call() «join» и получать их типы?
  • Изменить соединение?
  • Отправка разных параметров, таких как join<A> и строка?

Соответствующий код @NetMage, который я пытаюсь реализовать для этого вопроса (полная ссылка):

var aParm = typeof(A).Param("_a");
var aKeyFne = aParm.Lambda(aParm.Dot("bID"));

var bParm = typeof(B).Param("_b");
var bKeyFne = bParm.Lambda(bParm.Dot("ID"));
var anonType = (new { a = default(A), b = default(B) }).GetType();
var resultFne = (aParm, bParm).Lambda(anonType.New(aParm, bParm));
var join = db.A.AsConst().Join(db.B.AsConst(), aKeyFne, bKeyFne, resultFne);

.....

С использованием:

public static class ValueTupleExt {
    private static T[] makeArray<T>(params T[] itemArray) => itemArray;
    public static T[] ToArray<T>(this (T, T) tuple) => makeArray(tuple.Item1, tuple.Item2);    
}

public static class ExpressionExt {
    private static Type TQueryable = typeof(Queryable);

    private static Type TypeGenArg(this Expression e, int n) => e.Type.GetGenericArguments()[n];

    public static MethodCallExpression Join(this Expression outer, Expression inner, LambdaExpression outerKeyFne, LambdaExpression innerKeyFne, LambdaExpression resultFne) =>
            Expression.Call(TQueryable, "Join", new[] { outer.TypeGenArg(0), inner.TypeGenArg(0), outerKeyFne.ReturnType, resultFne.ReturnType }, outer, inner, outerKeyFne, innerKeyFne, resultFne);

    public static MethodCallExpression Select(this Expression src, LambdaExpression resultFne) => Expression.Call(TQueryable, "Select", new[] { src.TypeGenArg(0), resultFne.ReturnType }, src, resultFne);

    public static MethodCallExpression Where(this Expression src, LambdaExpression predFne) => Expression.Call(TQueryable, "Where", new[] { src.TypeGenArg(0) }, src, predFne);

    public static ConstantExpression AsConst<T>(this T obj) => Expression.Constant(obj, typeof(T));

    public static MemberExpression Dot(this Expression obj, string propNames) =>
        (MemberExpression)propNames.Split('.').Aggregate(obj, (ans, propName) => Expression.PropertyOrField(ans, propName));

    public static LambdaExpression Lambda(this ParameterExpression p1, Expression body) => Expression.Lambda(body, p1);
    public static LambdaExpression Lambda(this (ParameterExpression, ParameterExpression) parms, Expression body) => Expression.Lambda(body, parms.ToArray());

    public static NewExpression New(this Type t, params Expression[] vals) => Expression.New(t.GetConstructors()[0], vals, t.GetProperties());

    public static BinaryExpression opEq(this Expression left, Expression right) => Expression.Equal(left, right);

    public static ParameterExpression Param(this Type t, string pName) => Expression.Parameter(t, pName);
}

Помощь будет очень признательна.

Спасибо!


person Diogo Almeida    schedule 03.11.2020    source источник