使用传入的参数作为搜索字段,使用 Entity Framework Web API 创建动态搜索查询

问题描述 投票:0回答:4

您好,我想使用实体创建一个动态搜索查询,并将 URI 中的值作为搜索字段。

下面的代码可以工作,但我必须搜索的一些表有超过 200 个字段,我想创建一些可以获取属性名称并允许我搜索的内容。

它还可以有多个搜索选项,例如姓氏和名字以及出生日期

queryable = queryable.Where(x => x.<PROPERTY.NAME>.Contains(query.<PROPERTY.NAME>));

这可能吗?

这是我到目前为止的代码。

public List<EMPLOYEE> Get([FromUri] EMPLOYEE query)
    {

        List<EMPLOYEE> emps = new List<EMPLOYEE>();
        var db = AuthHandler.Ent;
        var queryable = db.EMPLOYEES.AsExpandable();

        foreach (var prop in query.GetType().GetProperties())
        {

            if (prop.GetValue(query, null) != null)
            {

                switch (prop.Name)
                {
                    case "EMP_CREATIONDATE":
                        queryable = queryable.Where(x => x.EMP_CREATIONDATE.Equals(query.EMP_CREATIONDATE));
                        break;
                    case "EMP_SURNAME":
                         queryable = queryable.Where(x => x.EMP_SURNAME.Contains(query.EMP_SURNAME));
                        break;
                    case "EMP_GIVENNAMES":
                        queryable = queryable.Where(x => x.EMP_GIVENNAMES.Contains(query.EMP_GIVENNAMES));
                        break;
                }
                queryable = queryable.Where(x => x.EMP_SURNAME.Contains(query.EMP_SURNAME));

            }
        }
        emps = queryable.ToList();

        return emps;

    }
c# entity-framework linq asp.net-web-api linq-to-entities
4个回答
2
投票

我将使用这样的谓词构建器

public static class PredicateBuilder
{
    public static Expression<Func<T, bool>> True<T>() { return f => true; }
    public static Expression<Func<T, bool>> False<T>() { return f => false; }

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                        Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
    }

    public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                         Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>
              (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
    }
}

并像这样使用它:

public IQueryable<Customer> GetCustomers(CustomerOrderSearchParameters parameters)
        {    
                context.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
                var predicate = PredicateBuilder.True<Customer>();
                if (!string.IsNullOrEmpty(parameters.FirstName))
                {
                    predicate = predicate.And(x => x.FirstName.Contains(parameters.FirstName));
                }
                if (!string.IsNullOrEmpty(parameters.LastName))
                {
                    predicate = predicate.And(x => x.LastName.Contains(parameters.LastName));
                }
                if (!string.IsNullOrEmpty(parameters.Email))
                {
                    predicate = predicate.And(x => x.email.Contains(parameters.Email));
                }
                if (!string.IsNullOrEmpty(parameters.PhoneNumber))
                {
                    predicate = predicate.And(x => x.MobilePhone.Contains(parameters.PhoneNumber) || x.HomePhone.Contains(parameters.PhoneNumber));
                }
                if (parameters.BrandID != null)
                {
                    predicate = predicate.And(x => x.Logins.Where(l => l.BrandID == parameters.BrandID).Any());
                }
                if (parameters.ShowNoOrders == true)
                {
                    predicate = predicate.And(x => x.Orders.Where(o => o.CustomerID != x.CustomerID).Any());
                }                
                return context.Customers.AsExpandable().Where(predicate);           
        }

此外,

CustomerOrderSearchParameters
只是一个简单参数列表(相当于您从查询中获得的参数)


1
投票

您可以使用

ObjectQuery
类和 Sql 运算符来构建动态查询,如下所示

public List<EMPLOYEE> Get([FromUri] EMPLOYEE query)
        {

            List<EMPLOYEE> emps = new List<EMPLOYEE>();
            var db = AuthHandler.Ent;
            var queryable = (ObjectQuery<EMPLOYEE>)db.EMPLOYEES.AsExpandable();

            string condition = "CONTAINS(@column, @search)";

            foreach (var prop in query.GetType().GetProperties())
            {
                var value = prop.GetValue(query, null);
                if (value != null)
                {
                    queryable = queryable.Where(string.Format(condition, prop.Name, value));
                }
            }
            emps = queryable.ToList();

            return emps;

        }

我说得对吗?


0
投票

我知道这个威胁已经很老了,可能已经有大量的库可供你使用,但它仍然可能对某人有所帮助:

[ApiController]
public class HrController : Controller
{
    /*Housekeeping stuff*/

    [HttpGet]
    [Produces("application/json")]
    [Route("api/[controller]/[action]/")]
    public async Task<IActionResult> Persons()
    {
        List<Person> ppr = await _context.People
            .AsNoTracking().ToListAsync();

        if (HttpContext.Request.Query != null)
        {
            foreach (var query in HttpContext.Request.Query)
            {
                if (typeof(PeoplePoolResource).GetProperty(query.Key) != null)
                {
                    if (query.Value.First() == null) continue;
                    if (query.Value.First().StartsWith('@')) //wildcard search
                    {
                        ppr = ppr.Where(a => a.GetType().GetProperty(query.Key).GetValue(a) != null &&
                            a.GetType().GetProperty(query.Key).GetValue(a).ToString().Contains(query.Value.First().Substring(1), System.StringComparison.OrdinalIgnoreCase)).ToList();
                        continue;
                    }
                    if (typeof(PeoplePoolResource).GetProperty(query.Key).PropertyType == typeof(int))
                    {
                        ppr = ppr.Where(a => (int)a.GetType().GetProperty(query.Key).GetValue(a) == int.Parse(query.Value)).ToList();
                    }
                    else if (typeof(PeoplePoolResource).GetProperty(query.Key).PropertyType == typeof(bool))
                    {
                        ppr = ppr.Where(a => (bool)a.GetType().GetProperty(query.Key).GetValue(a) == bool.Parse(query.Value)).ToList();
                    } //add other types like double
                    else
                    {
                        ppr = ppr.Where(a => a.GetType().GetProperty(query.Key).GetValue(a) != null &&
                            a.GetType().GetProperty(query.Key).GetValue(a).ToString().Equals(query.Value, System.StringComparison.OrdinalIgnoreCase)).ToList();
                    }
                }
            }
        }

        return Json(ppr);
    }
}

然后您可以发送带有查询的 get 请求,如下所示:

http://localhost:50409/api/hr/persons?Id=252(这会让您找到

Id
252 的人)

http://localhost:50409/api/hr/persons?FirstName=@John(这会让您在其

FirstName
字段中包含字母John(不区分大小写)的所有人员

优点:

  • 极其通用
  • 也可以扩展以接受正则表达式
  • 可以轻松更改为完全通用的函数,该函数接受
    List<T>
    并返回
    List<T>

缺点:

  • 这种方法对性能的要求不是很高
  • 有局限性,例如您不能使用大于或小于等进行搜索,但可以添加
  • 搜索是逻辑
    AND
    搜索,不能更改为逻辑
    OR
    或其他。

0
投票

查看开源包 ServiceQuery。 http://ServiceQuery.com 它提供了一个动态表达式生成器,可以通过 REST API 服务序列化复杂的查询。

您的 API 服务:

using ServiceQuery;

[HttpPost]
[Route("ServiceQuery")]
public ServiceQueryResponse<ExampleTable> ServiceQuery(ServiceQueryRequest request)
{    
    var queryable = _context.ExampleTable.AsQueryable();
    return request.Execute(queryable);
}

您的客户:

<script src="/js/servicequery.js"></script>
<script type="text/javascript">
    function GetAll() {
        var request = new ServiceQueryRequestBuilder()
            .IsEqual("ID","1234").And().StartsWith("FirstName","John").Build();
        $.ajax({
            url: '/api/Example/ServiceQuery',
            data: JSON.stringify(request),
            type: "POST",
            dataType: 'json',
            headers: { 'Content-Type': 'application/json' },
            success: function (result) {
                alert(result.list.length + ' records returned');
            }
       });
    }
</script>
© www.soinside.com 2019 - 2024. All rights reserved.