Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

c# - IQueryable<T> extension method for advanced where

I'm trying to add extension method for IQueryable<T> in order to attach specific where to IQueryable.

Assuming user is searching for data with name equal to "Matt"

public IEnumerable<Employees> Search(String name)
{
    var qList = _context.Employees;
    if(!String.isNullOrEmpty(name))
        qList = qList.Where(emp => emp.Name == name);
    return qList.ToList();
}

Now if user is searching for data with name that starts with "Matt" - He will probably try to write something like "Matt%**" or "Matt*" in a specific textbox. to predict this I can do:

public IEnumerable<Employees> Search(String name)
{
    var qList = _context.Employees;
    if(!String.isNullOrEmpty(name))
    {
        string extractedName = name.Replace("%","").Replace("*","");
        if(name.EndsWith("%") || name.EndsWith("*");
            qList = qList.Where(emp => emp.Name.StartsWith(extractedName));
    }

    return qList.ToList();
}

A lot of IF statements will be here to predict all of possibilities but OK. I can do that. but what I don't like is to repeat my code every time I do such feature.

How can i make extension method for

IQueryable<T>

which will do this checks all the time for me?

public static IQueryable<T> MyAdvancedSearch<T>(this IQueryable<T> qList, String searchText)
{
    if(!String.isNullOrEmpty(searchText))
    /// ... Problem starts here.. because I cannot make this "Where": 
    qList.Where(prop=>prop.??? == searchText);
    /// ... I dont know how to tell my method which field should be filtered
}

UPDATE I've managed to create a extension method which looks OK:

        public static IQueryable<T> Filter<T>(this IQueryable<T> qList, Func<T,string> property, string text )
        {
            if (!String.IsNullOrEmpty(text))
            {
                if ((text.StartsWith("%") || text.StartsWith("*")) && (text.EndsWith("*") || text.EndsWith("*")))
                    qList = qList.Where(e => property(e).Contains(text));

                else if (text.StartsWith("%") || text.StartsWith("*"))
                    qList = qList.Where(e => property(e).EndsWith(text));

                else if (text.EndsWith("%") || text.EndsWith("*"))
                    qList = qList.Where(e => property(e).StartsWith(text));
                else
                    qList = qList.Where(e => property(e) == text);
            }
            return qList;
        }

but when I try to do qList.Tolist() I'm receiving error:

InvalidOperationException: The LINQ expression 'DbSet<Employee>()
.Where(o => Invoke(__property_0, o)
== __text_1)' could not be translated.

InvalidOperationException: The LINQ expression 'DbSet<Employee>() .Where(o => Invoke(__property_0, o) == __text_1)' 
could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. 
See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
question from:https://stackoverflow.com/questions/65951293/iqueryablet-extension-method-for-advanced-where

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

This should do the trick

public static IQueryable<T> MyAdvancedSearch<T>(this IQueryable<T> qList, Expression<Func<T, string>> property, String text)
{
    Expression<Func<T, bool>> expression = null;

    var propertyName = GetPropertyName(property);
    var parameter = Expression.Parameter(typeof(T), "x");
    var left = propertyName.Split('.').Aggregate((Expression)parameter, Expression.Property);
    var right = Expression.Constant(text, typeof(string));
    Expression body = null;

    if (!string.IsNullOrEmpty(text))
    {
        if ((text.StartsWith("%") || text.StartsWith("*")) && (text.EndsWith("%") || text.EndsWith("*")))
            body = Expression.Call(left, "Contains", Type.EmptyTypes, Expression.Constant(text, typeof(string)));

        else if (text.StartsWith("%") || text.StartsWith("*"))
                body = Expression.Call(left, "EndsWith", Type.EmptyTypes, Expression.Constant(text, typeof(string)));

        else if (text.EndsWith("%") || text.EndsWith("*"))
                body = Expression.Call(left, "StartsWith", Type.EmptyTypes, Expression.Constant(text, typeof(string)));
        else
                body = Expression.MakeBinary(ExpressionType.Equal, left, right);
    }

    expression = Expression.Lambda<Func<T, bool>>(body, parameter);

    qList = qList.Where(expression);
        return qList;
}
public static string GetPropertyName<TClass, TProperty>(Expression<Func<TClass, TProperty>> property)
{
    MemberExpression member = property.Body as MemberExpression;
    PropertyInfo info = member.Member as PropertyInfo;

    return info.Name;
}
public static IQueryable<T> MyAdvancedSearch<T>(this IQueryable<T> qList, Func<T, string> func, String searchText) { qList.Where(prop => func(prop) == searchText); return qList; } Then you can call it like this:
var qList = _context.Employees.MyAdvancedSearch((emp) => emp.Name, extractedname)

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...