2012-09-03 55 views
1

我有一堆這些任務都基於LINQ查詢。我正在尋找好的方法重構他們,使他們更容易閱讀,並讓我改變了查詢依賴於語言/區域等分割長linq查詢以提高可維護性

var mailTaskOne = CreateTask(() => myService.Mail.Where(p => p.ProjectName == "Delta" 
    && (p.MailLang== (int)MailLanguage.EU || p.MailLang == (int)MailLanguage.RU) 
    && (p.DateEntered >= startDate && p.DateEntered <= endDate) 
    && p.MailPriority == (int)MailPriority.High).Count()); 

一個我覺得很方便。將分裂的方式查詢到這樣的東西。

var results = myService.Mail.Where(x => x.ProjectName == "Delta"); 
results = results.Where(p => p.MailLang== (int)MailLanguage.EU); 
results = results.Where(p => p.DateModified >= startDate && p.DateModified <= endDate); 

這將允許我這樣做,而無需重複每個區域的整個查詢。

if (MailLanguage == "English") 
    results = results.Where(p => p.MailLang== (int)MailLanguage.EU); 
else 
    results = results.Where(p => p.MailLang== (int)MailLanguage.RU); 

有沒有人知道更好的解決方案呢?根據需求,我最終可能需要做20個這樣的查詢,如地區,項目名稱等


編輯:

由於一些限制,我不知道與後端(Web服務/ API)我也很遺憾不能使用一些在這個問題中提到的真棒答案。

例如,這不會得到正確的翻譯,但在任何情況下,因爲答案不正確,根本不適用於我正在對付的API - 可能是因爲它實施得不好。

public bool IsValid(Type x) 
{ 
    return (x.a == b) && (x.c ==d) && (x.d == e); 
} 

無論如何,任何人尋找類似的解決方案所有這些都是有效的答案,但在最後,我結束了與類似提供的解決方案snurre一些事情。

+0

爲什麼你不使用語言作爲參數/變量? –

+0

隨時可以進一步解釋 - 我的頭腦目前處於空白狀態。請記住,可能有多種語言,例如英語可能是US + EU。 :] – eandersson

+0

您是在查詢數據庫還是內存? –

回答

1

您可以創建一個參數類,如:

public class MailParameters 
{ 
    public DateTime EndTime { get; private set; } 
    public IEnumerable<int> Languages { get; private set; } 
    public int Priority { get; private set; } 
    public string ProjectName { get; private set; } 
    public DateTime StartTime { get; private set; } 

    public MailParameters(string projectName, DateTime startTime, DateTime endTime, MailLang language, Priority priority) 
     : this(projectName, startTime, endTime, new[] { language }, priority) 

    public MailParameters(string projectName, DateTime startTime, DateTime endTime, IEnumerable<MailLang> languages, Priority priority) 
    { 
     ProjectName = projectName; 
     StartTime = startTime; 
     EndTime = endTime; 
     Languages = languages.Cast<int>(); 
     Priority = (int)priority; 
    } 
} 

然後將這些擴展方法:

public static int Count(this IQueryable<Mail> mails, MailCountParameter p) 
{ 
    return mails.Count(m => 
     m.ProjectName == p.ProjectName && 
     p.Languages.Contains(m.MailLang) && 
     m.EnteredBetween(p.StartTime, p.EndTime) && 
     m.Priority == p.Priority); 
} 

public static bool EnteredBetween(this Mail mail, DateTime startTime, DateTime endTime) 
{ 
    return mail.DateEntered >= startTime && mail.DateEntered <= endTime; 
} 

的使用將被:

var mailParametersOne = new MailParameters("Delta", startDate, endDate, new[] { MailLang.EU, MailLang.RU }, MailPriority.High); 
var mailTaskOne = CreateTask(() => myService.Mail.Count(mailParametersOne)); 
2

我只想把查詢分解成像你所建議的不同的行,這意味着你可以把每行註釋描述它在做什麼。您仍然只是一次訪問數據庫,因此您在性能方面沒有任何損失,但獲得了更好的可讀性。

+0

謝謝。我自己也這麼認爲,但是想確保我沒有漏掉任何明顯的東西。 – eandersson

1

考慮將複雜的比較轉換爲函數。對於exanple,代替

Results.Where(x => (x.a == b) && (x.c == d) && (x.d == e)) 

考慮

Results.Where(x => IsValid(x)) 

... 

public bool IsValid(Type x) 
{ 
    return (x.a == b) && (x.c ==d) && (x.d == e); 
} 

該代碼變得更可讀的和的IsValid易於使用自動化測試框架進行測試。

+3

一個更可讀的版本將是:Results.Where(IsValid) – snurre

+0

@snurre你是對的。 – akton

+0

謝謝!你們並不是很容易就可以選擇正確的答案,這麼多好的答案! :D – eandersson

2

爲什麼不簡單地有一個目的的方法?

public static IQueryable<Mail> Count(this IQueryable<Mail> mails, 
        string projectName, 
        MailLanguage mailLanguage, 
        DateTime startDate, 
        DateTime endDate) { 
    return mails.Count(p=> 
      p.ProjectName == projectName 
      && p.MailLang == mailLanguage 
      && p.DateEntered >= startDate 
      && p.DateEntered <= endDate 
      && p.MailPriority == (int)MailPriority.High); 
} 

,那麼你可以簡單地使用它像這樣

CreateTask(() => myService.Mail.Count("Delta",MailLanguage.EU,startDate,endDate)); 
2

你可以把項目名稱,修改數據,郵件語言和任何其他標準爲變量和guive他們基於任何條件所需的值。然後你的查詢將使用變量而不是文字值。

var projectName="Delta"; 
var mailLanguage=(int)MailLanguage.RU; 

var results=myService.Mail.Where(x => x.ProjectName == projectName) 
      && (p.MailLang== mailLanguage); 

通過這種方式,您可以將大部分複雜性賦予變量,並且linq查詢將更易於閱讀和維護。

0

我的最終解決方案是基於在ScottGu的一篇文章上。 http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

我構建這樣的LINQ查詢。

var linqStatements = new List<String>(); 

    linqStatements.Add(parser.StringToLinqQuery<Project>("ProjectId", report.Project)); 
    linqStatements.Add(parser.StringToLinqQuery<Region>("RegionId", report.Region)); 
    linqStatements.Add(parser.StringToLinqQuery<Status>("Status", report.Status)); 
    linqStatements.Add(parser.StringToLinqQuery<Priority>("Priority", report.Priority)); 
    linqStatements.Add(parser.StringToLinqQuery<Category>("CategoryId", report.Category)); 
    linqStatements.Add(AccountIdsToLinqQuery(report.PrimaryAssignment)); 

    string baseQuery = String.Join(" AND ", linqStatements.Where(s => !String.IsNullOrWhiteSpace(s))); 
    var linqQuery = service.Mail.Where(baseQuery).Cast<Mail>(); 

StringToLinqQuery看起來像這樣(簡化版)。

public string StringToLinqQuery<TEnum>(string field, string value) where TEnum : struct 
{ 
    if (String.IsNullOrWhiteSpace(value)) 
     return String.Empty; 

    var valueArray = value.Split('|'); 
    var query = new StringBuilder(); 

    for (int i = 0; i < valueArray.Count(); i++) 
    { 
     TEnum result; 
     if (Enum.TryParse<TEnum>(valueArray[i].ToLower(), true, out result)) 
     { 
      if (i > 0) 
       query.Append(" OR "); 
      query.AppendFormat("{0} == {1}", field, Convert.ToInt32(result)); 
     } 
     else 
     { 
      throw new DynoException("Item '" + valueArray[i] + "' not found. (" + type of (TEnum) + ")", 
            query.ToString()); 
     } 
    } 

    // Wrap field == value with parentheses() 
    query.Insert(0, "("); 
    query.Insert(query.Length, ")"); 

    return query.ToString(); 
} 

最終的結果看起來像這樣。

service.Mail.Where("(ProjectId == 5) AND (RegionId == 6 OR RegionId == 7) AND (Status == 5) and (Priority == 5)") 

在我的項目我的值存儲在一個XML文件,然後將它們送入上述LINQ查詢。如果一個字段爲空,它將被忽略。它還支持使用|符號的多個值,例如, EU|US將轉化爲(Region == 5 OR Region == 6)