В этой единственной строке кода есть много недостатков:
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);