c# - 从字符串定义的表名中获取根据字符串定义的字段过滤的数据

标签 c# asp.net-mvc entity-framework linq

我正在尝试根据值数组中的动态字段名称返回动态类型的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);
    }

最后一行给了我一个错误:

Late bound operations cannot be performed on types or methods for which ContainsGenericParameters is true.

老实说,今天下午我根据互联网上的片段拼凑了这个。我不知道我在这里做什么,这对我来说是新的,我很想学习。只是为了避免 SQL 注入(inject),项目的其余部分完全是 Linq,所以我会继续努力。我也在学习通用类型,我对此很感兴趣,但不知道如何在这里使用它们。

最佳答案

这行代码有很多缺陷:

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 构造一个泛型方法并调用它。

  • 尝试通过反射调用静态通用扩展方法,就好像它是实例方法一样。相反,您应该将 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);

关于c# - 从字符串定义的表名中获取根据字符串定义的字段过滤的数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50415229/

相关文章:

c# - 将一幅图像中的 SURF 描述符与其他图像中的描述符列表进行比较

java - JNDI .Net 实现

c# - LINQ-NHibernate - 只为复杂对象选择几个字段(包括集合)

c# - RESTful 设计是否更喜欢 [FromBody] 标签?

c# - 为什么我不能将类库与 Asp.Net 5 MVC 6 项目中的 EF 6.1.3 一起使用

c# - IsDirty 对 EF 实体使用 INotifyPropertyChanged

C#二进制移位自动旋转

c# - 何时选择从代码隐藏而不是外部 js 文件进行 Javascript 注入(inject)

asp.net-mvc - MVC3 session 开始事件触发两次

silverlight - 使用 Silverlight 在 RIA 服务中用一 block 石头杀死两只鸟