Получение данных из строкового имени таблицы, отфильтрованного по строковому полю - PullRequest
0 голосов
/ 18 мая 2018

Я пытаюсь вернуть содержимое таблицы отфильтрованного динамического типа Where на основе имени динамического поля из массива значений.

Вот что у меня есть:

public JsonResult GetRelationShips(string linkingTable, string idField, int[] ids)
    {
        var tableType = typeof(context).GetProperty(linkingTable);

        var entityTable = tableType.GetValue(db) as IQueryable;

        var method = typeof(List<int>).GetMethod("Contains");

        var eParam = Expression.Parameter(tableType.PropertyType.GetGenericArguments()[0]);

        var call = Expression.Call(Expression.Constant(ids.ToList()), method, Expression.Property(eParam, idField));

        var func = typeof(Func<,>);

        var genericFunc = func.MakeGenericType(tableType.PropertyType.GetGenericArguments()[0], typeof(bool));

        var lambda = Expression.Lambda(genericFunc, call, eParam);


        var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });

        return Json(results);
    }

Эта последняя строка дает мне ошибку:

Операции с поздним связыванием не могут быть выполнены для типов или методов, для которых ContainsGenericParameters имеет значение true.

Честно говоря, я сделал этовместе во второй половине дня из фрагментов со всего Интернета.Я понятия не имею, что я делаю здесь, это ново для меня, и я стремлюсь учиться.Просто пытаясь избежать внедрения SQL, остальная часть проекта полностью Linq, так что я продолжаю.Я тоже изучаю Дженерики, разбираюсь с этим, но не понимаю, как их здесь использовать.

1 Ответ

0 голосов
/ 19 мая 2018

В этой единственной строке кода есть много недостатков:

var results = typeof(System.Linq.Enumerable).GetMethods().Where(x => x.Name == "Where").First().Invoke(db, new object[] { lambda });
  • Попытка вызвать Enumerable.Where вместо Queryable.Where.Это приведет к извлечению данных всей таблицы и выполнению фильтрации в памяти, а не на стороне базы данных.

  • Попытка вызова потенциально неправильного метода.Where имеет 2 перегрузки, и не определено, какая из них будет возвращена отражением в качестве первой.

  • Попытка вызвать определение общего метода, вызывая полученное вами исключение.Сначала вы должны сконструировать универсальный метод с помощью MakeGenericMethod и вызвать его.

  • Попытка вызвать static универсальный метод расширения с помощью отражения, как если бы он был экземпляр метод.Вместо этого вы должны передать null в качестве первого аргумента Invoke и передать new object[] { entityTable, lambda } в качестве второго аргумента.

Вы можете избежать всех этих ловушек, просто используя динамический метод отправки C #:

IQueryable results = Queryable.Where((dynamic)entityTable, (dynamic)lambda);

Весь код можно упростить, используя следующую перегрузку Expression.Call:

public static MethodCallExpression Call(
    Type type, 
    string methodName,
    Type[] typeArguments,
    params Expression[] arguments);

, что очень полезно для "вызова" статического универсального кодаметоды расширения:

var query = (IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db);
// e =>
var entity = Expression.Parameter(query.ElementType, "e");
// ids.Contains(e.idField)
// = Enumerable<int>.Contains(ids, e.idField)
var containsCall = Expression.Call(
    typeof(Enumerable),
    nameof(Enumerable.Contains),
    new Type[] { typeof(int) },
    Expression.Constant(ids),
    Expression.Property(entity, idField)
);
// e => ids.Contains(e.idField)
var predicate = Expression.Lambda(containsCall, entity);
// query = query.Where(predicate);
query = Queryable.Where((dynamic)query, (dynamic)predicate);

Вы также можете избежать динамического вызова Where и использовать аналогичный Expression.Call подход, основанный на «вызове», в сочетании с IQueryProvider.CreateQuery:

// query.Where(predicate)
// = Queryable.Where<ElementType>(query, predicate)
var whereCall = Expression.Call(
    typeof(Queryable),
    nameof(Queryable.Where),
    new Type[] { query.ElementType },
    query.Expression,
    predicate
);
// query = query.Where(predicate)
query = query.Provider.CreateQuery(whereCall);

Я предоставил все это только потому, что вы сказали, что хотите учиться.Самый простой способ справиться с такими задачами (а не изобретать велосипед) - использовать какой-то пакет сторонних разработчиков.Например, с пакетом System.Linq.Dynamic весь код будет:

var query = ((IQueryable)db.GetType().GetProperty(linkingTable).GetValue(db))
    .Where($"@0.Contains({idField})", ids);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...