c# - 进行通用 MVC API 查询

标签 c# entity-framework lambda .net-core

我正在 .NET Core 中构建 MVC API。我有几个非常大的表(第 3 方系统),我试图允许将任何列的名称作为 URL 参数传递,并在 Where 子句中使用一个值。这使用 Entity Framework 和 .NET Core。

想法是抓取与参数名称匹配的列,并在带有参数值的 Where 子句中使用它。我希望 Where 查询中的 lambda 最终看起来像:

//GET: api/DWRWorkItems?Parameter=Value
...
dWRWorkItem = dWRWorkItem.Where(m => 
   m.Parameter == Value
);

这是我尝试的一个干净的解决方案,来自 Controller 的路由代码。模型和 View 与 Entity Framework 创建的默认值没有变化。

    // GET: api/DWRWorkItems
    [HttpGet]
    public IEnumerable<DWRWorkItem> GetTDwrWrkItm()
    {
        IQueryable<DWRWorkItem> dWRWorkItem = _context.TDwrWrkItm.Where(m => 1 == 1);
        var q = HttpContext.Request.Query;
        foreach (string p in q.Keys)
        {

            dWRWorkItem = dWRWorkItem.Where(m => 
                m.GetType().GetProperty(p).GetValue(m, null) == q[p]
            );

        }

        return dWRWorkItem.ToList();
    }

Intellisense (VS 2017) 预计不会出现错误,但是当我运行它时,我得到:

The operands for operator 'Equal' do not match the parameters of method 'op_Equality'.

单步执行代码,lambda 中的反射似乎没有按预期工作。这是 dWRWorkItem.Expression.Arguments[1] 的样子:

{m => (GetProperty(m.GetType(), value(EBOOKS.Controllers.DWRWorkItemsController+<>c__DisplayClass0#1).p).GetValue(m, null) == value(EBOOKS.Controllers.DWRWorkItemsController+<>c__DisplayClass0#1).CS$<>8__locals1.q.get_Item(value(EBOOKS.Controllers.DWRWorkItemsController+<>c__DisplayClass0#1).p))}

虽然参数不是动态的示例如下所示:

{m => (m.ContId == value(EBOOKS.Controllers.DWRWorkItemsController+<>c__DisplayClass3_0).id)}

最佳答案

一般来说,除 LINQ to Objects 之外的查询提供程序不喜欢表达式树内部的反射调用,因此最好使用 System.Linq.Expressions.Expression 动态组合表达式类方法。

这是适合您的情况的自定义扩展方法:

public static partial class QueryableExtensions
{
    public static IQueryable<T> WhereEquals<T>(this IQueryable<T> source, string memberPath, string value)
    {
        var parameter = Expression.Parameter(typeof(T), "e");
        var left = memberPath.Split('.').Aggregate((Expression)parameter, Expression.PropertyOrField);
        var right = Expression.Constant(ToType(value, left.Type), left.Type);
        var predicate = Expression.Lambda<Func<T, bool>>(Expression.Equal(left, right), parameter);
        return source.Where(predicate);
    }

    private static object ToType(string value, Type type)
    {
        if (type == typeof(string)) return value;
        if (string.IsNullOrEmpty(value)) return null;
        return Convert.ChangeType(value, Nullable.GetUnderlyingType(type) ?? type);
    }
}

除了使用 Expression.Equal(相当于 == 运算符)之外,它还必须将 string 处理为实际值类型转换,顺便说一句,这是你原来的异常的原因。

用法是这样的:

var dWRWorkItem = _context.TDwrWrkItm.AsQueryable();
var q = HttpContext.Request.Query;
foreach (string p in q.Keys)
    dWRWorkItem = dWRWorkItem.WhereEquals(p, q[p]);    
return dWRWorkItem.ToList();

关于c# - 进行通用 MVC API 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42935150/

相关文章:

c# - Entity Framework 日期时间和 UTC

entity-framework - EF Core,从netcore2.2更新到netcore3.1后出现无效列名异常

c# - LINQ-to-SQL : Convert Func<T, T, bool> 到表达式<Func<T, T, bool>>

c# - 运行时的 Expression.Lambda 和查询生成,最简单的 "Where"示例

c++ - 将类型名传递给 lambda 最简洁的方法是什么?

c# - 在 C# 中检测空白图像(预期的图像是条形码)

C# 将对象转换为基类列表

c# - 关于 Telerik 报告

c# - 如何使用通用存储库、工作单元模式和多个数据库的多个 EDMX

javascript - 如何增加并发异步调用的数量?