动态表达EF核心“赞”功能

时间:2018-09-06 18:44:55

标签: c# .net-core asp.net-core-2.0 asp.net-core-webapi ef-core-2.0

我已经编写了一些代码来制作动态表达式以过滤分页。 我正在尝试动态表达EF Core内置函数以进行搜索(EF.Functions.Like)。

我尝试了类似bottom的方法,但这是一种扩展方法,调用该方法时不使用first参数。我不知道该怎么做==> Ef => Function => Like。
该方法应这样使用=> Ef.Functions.Like("Property to search", "%Some Pattern")

var likeMethod = typeof(DbFunctionsExtensions)
                        .GetMethods()
                        .Where(p => p.Name == "Like")
                        .First();
string pattern = $"%{finalConstant}%"; 

ConstantExpression likeConstant = Expression.Constant(pattern,typeof(string));

// the member expression is the property expression for example p.Name
var likeMethodCall = Expression.Call(method: likeMethod, arguments: new[] { memberExpression, likeConstant });

var searchLambda = Expression.Lambda<Func<T, bool>>(likeMethodCall, parameter);
query = query.Where(searchLambda);

但是它抛出异常

  

为调用方法'Boolean Like(Microsoft.EntityFrameworkCore.DbFunctions,System.String,   System.String)'\ r \ n参数名称:方​​法

2 个答案:

答案 0 :(得分:1)

我根据本文实现了动态搜索 [.NET Core Npgsql.EntityFrameworkCore ILikeExpression] [1] [1]:https://stackoverflow.com/questions/52038380/net-core-npgsql-entityframeworkcore-ilikeexpression 那就是我所做的:

我实现了[Searchable]属性,通过该属性我将标记执行搜索的属性。属性仅是字符串类型的,如有必要,我可以解释一下如何搜索long和int类型的属性。

[AttributeUsage(AttributeTargets.Property)]
public class SearchableAttribute : Attribute
{
}

已为IQueryable创建了扩展名,该扩展名从搜索中获取输入字符串并根据指定的属性实现Like函数

public static class QueryableExtension
{
    public static IQueryable<TEntityDto> ExecuteQueryFilter<TEntityDto>(this IQueryable<TEntityDto> queryable, string query)
        where TEntityDto : class, IEntityDto
    {
        // If the incoming request is empty, skip the search
        if (string.IsNullOrEmpty(query))
        {
            return queryable;
        }

        // We get all properties with type of string marked with our attribute
        var properties = typeof(TEntityDto).GetProperties()
            .Where(p => p.PropertyType == typeof(string) &&
                        p.GetCustomAttributes(typeof(SearchableAttribute), true).FirstOrDefault() != null)
            .Select(x => x.Name).ToList();

        // If there are no such properties, skip the search
        if (!properties.Any())
        {
            return queryable;
        }

        // Get our generic object
        ParameterExpression entity = Expression.Parameter(typeof(TEntityDto), "entity");

        // Get the Like Method from EF.Functions
        var efLikeMethod = typeof(DbFunctionsExtensions).GetMethod("Like",
            BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic,
            null,
            new[] { typeof(DbFunctions), typeof(string), typeof(string) },
            null);

        // We make a pattern for the search
        var pattern = Expression.Constant($"%{query}%", typeof(string));

        // Here we will collect a single search request for all properties
        Expression body = Expression.Constant(false);

        foreach (var propertyName in properties)
        {
            // Get property from our object
            var property = Expression.Property(entity, propertyName);

            // Сall the method with all the required arguments
            Expression expr = Expression.Call(efLikeMethod,
                    Expression.Property(null, typeof(EF), nameof(EF.Functions)), property, pattern);

            // Add to the main request
            body = Expression.OrElse(body, expr);
        }

        // Compose and pass the expression to Where
        var expression = Expression.Lambda<Func<TEntityDto, bool>>(body, entity);
        return queryable.Where(expression);
    }
}

Dto对象本身看起来像这样:

public class CategoryDto : IEntityDto
{
    public long Id { get; set; }

    [Searchable]
    public string Name { get; set; }

    [Searchable]
    public string IconKey { get; set; }

    public long UploadId { get; private set; }

    [Searchable]
    public string UploadFileName { get; set; }

    [Searchable]
    public string CreatedBy { get; set; }
    public DateTime Created { get; set; }
}

我在一百万条记录中测试了这种搜索方法,对象名称用一到五个词表示。搜索过程非常快。这里的性能优势是Expression在数据库端将LINQ转换为SQL

答案 1 :(得分:0)

这是一个可行的例子

public static Expression<Func<T, bool>> Like<T>(Expression<Func<T, string>> prop, string keyword)
{
    var concatMethod = typeof(string).GetMethod(nameof(string.Concat), new[] { typeof(string), typeof(string) });
    return Expression.Lambda<Func<T, bool>>(
        Expression.Call(
            typeof(DbFunctionsExtensions),
            nameof(DbFunctionsExtensions.Like),
            null,
            Expression.Constant(EF.Functions),
            prop.Body,
            Expression.Add(
                Expression.Add(
                    Expression.Constant("%"),
                    Expression.Constant(keyword),
                    concatMethod),
                Expression.Constant("%"),
                concatMethod)),
        prop.Parameters);
}
query = query.Where(Like<User>(u => u.UserName, "angel"));