簡體   English   中英

協助重構LINQ方法

[英]Assistance with refactoring a LINQ method

我需要一些幫助,我的方法在我的課程中重復了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;
    }

你可以做的是使用一個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);
}

其中使用以下方法將一個表達式的所有實例替換為另一個:

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);
    }
}

現在你可以寫:

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;
}

另外,您應該使用enum來表示searchFilter的不同類型的過濾器,而不是字符串。 這將使調用者更容易,因為他們不需要輸入確切的字符串而沒有任何好的方法知道確切的選項是什么,或者提供的選項是否有效。

此版本允許您將屬性傳遞給過濾器,如下所示:

Filter(models, (MyModel m) => m.FirstName, "Joe", "contains"); 
Filter(models, (MyModel m) => m.LastName, "Smith", "contains"); 

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

    return query;
}

我會做以下一項或多項:

  1. 反射並緩存屬性查找“方法”(“x.FirstName”位可以很容易地通過反射處理

  2. 使用“選擇器”功能並應用此功能來拾取目標對象

  3. 使這成為一種擴展方法

碼:

public static class FilterPerson
{
    static IQueryable<Person> FilterPerson(
       this IQueryable<Person> query,
       FilterString filter,
       Func<Person, string> selector,
        string searchText)
    {
        var enumerableQuery = query.AsEnumerable();
        switch (filter)
        {
            case FilterString.Contains:
                enumerableQuery = enumerableQuery.Where(x => selector(x).ToLowerInvariant().Contains(searchText.ToLowerInvariant()));
                break;
            case FilterString.DoesNotContain:
                enumerableQuery = enumerableQuery.Where(x => !selector(x).ToLower().Contains(searchText.ToLower()));
                break;
            case FilterString.StartsWith:
                enumerableQuery = enumerableQuery.Where(x => selector(x).StartsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case FilterString.EndsWith:
                enumerableQuery = enumerableQuery.Where(x => selector(x).EndsWith(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
            case FilterString.Equals:
                enumerableQuery = enumerableQuery.Where(x => selector(x).Equals(searchText, StringComparison.InvariantCultureIgnoreCase));
                break;
        }

        return enumerableQuery.AsQueryable();
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string PhoneNumber { get; set; }

}

public enum FilterString
{
    StartsWith,
    Contains,
    DoesNotContain,
    EndsWith,
    Equals
}

如果它在你的代碼中被調用了6次,並且里面有5個路徑,我會說這個方法本身引入了不必要的耦合,應該被刪除。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM