协助重构LINQ方法

Mad*_*501 5 c# linq asp.net

我需要一些帮助,我的方法在我的课程中重复了6次,每个方法中唯一改变的是LINQ属性(在示例中为"名字",但我也有一个用于姓氏,公司名称,用户ID,状态).我想帮助重构一下,这样我就可以只使用一种方法,使属性成为动态或传入.

private static IQueryable<MyModel> FilterFirstName(IQueryable<MyModel> query, string searchText, string searchFilter)
    {
        switch (searchFilter.ToLower())
        {
            case "contains":
                query = query.Where(x => x.FirstName.ToLower().Contains(searchText.ToLower()));
                break;
            case "does not contain":
                query = query.Where(x => !x.FirstName.ToLower().Contains(searchText.ToLower()));
                break;
            case "starts with":
                query = query.Where(x => x.FirstName.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case "ends with":
                query = query.Where(x => x.FirstName.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case "equals":
                query = query.Where(x => x.FirstName.Equals(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
        }

        return query;
    }
Run Code Online (Sandbox Code Playgroud)

Ser*_*rvy 2

您可以做的是使用一种Compose可以将一个表达式与另一个表达式组合的方法:

public static Expression<Func<TFirstParam, TResult>>
    Compose<TFirstParam, TIntermediate, TResult>(
    this Expression<Func<TFirstParam, TIntermediate>> first,
    Expression<Func<TIntermediate, TResult>> second)
{
    var param = Expression.Parameter(typeof(TFirstParam), "param");

    var newFirst = first.Body.Replace(first.Parameters[0], param);
    var newSecond = second.Body.Replace(second.Parameters[0], newFirst);

    return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
Run Code Online (Sandbox Code Playgroud)

它使用以下方法将一个表达式的所有实例替换为另一个表达式:

public static Expression Replace(this Expression expression,
    Expression searchEx, Expression replaceEx)
{
    return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
    private readonly Expression from, to;
    public ReplaceVisitor(Expression from, Expression to)
    {
        this.from = from;
        this.to = to;
    }
    public override Expression Visit(Expression node)
    {
        return node == from ? to : base.Visit(node);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在你可以写:

private static IQueryable<MyModel> FilterFirstName(
    IQueryable<MyModel> query,
    Expression<Func<MyModel, string>> selector,
    string searchText,
    string searchFilter)
{
    switch (searchFilter.ToLower())
    {
        case "contains":
            query = query.Where(selector.Compose(
                text => text.ToLower().Contains(searchText.ToLower())));
            break;
        case "does not contain":
            query = query.Where(selector.Compose(
                text => !text.ToLower().Contains(searchText.ToLower())));
            break;
        case "starts with":
            query = query.Where(selector.Compose(
                text => text.StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
        case "ends with":
            query = query.Where(selector.Compose(
                text => text.EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
        case "equals":
            query = query.Where(selector.Compose(
                text => text.Equals(searchText, StringComparison.InvariantCultureIgnoreCase)));
            break;
    }

    return query;
}
Run Code Online (Sandbox Code Playgroud)

顺便说一句,您实际上应该使用 anenum来表示 的不同类型的过滤器searchFilter,而不是字符串。这将使调用者变得更加容易,因为他们不需要键入精确的字符串,而无需任何好的方法来知道确切的选项是什么,或者提供的选项是否有效。