2016-02-13 101 views
1

我開發一個ASP.NET MVC的網站,並正在尋找一種方法來改善這個程序。可以在LINQ級別或SQL Server級別上進行改進。我希望我們可以在一個查詢調用中做到這一點。此查詢是否可以改進查找丟失的密鑰? (SQL或LINQ)

這裏是所涉及的表和一些示例數據: enter image description here

我們沒有限制,每Key必須有每個LanguageId價值,堪與業務邏輯不允許這樣contraint。但是,在應用程序級別,我們要警告管理員,某個鍵缺少某些語言值。所以,我有這個類和查詢:

public class LocalizationKeyWithMissingCodes 
{ 
    public string Key { get; set; } 
    public IEnumerable<string> MissingCodes { get; set; } 
} 

這種方法得到的密鑰列表,以及任何遺漏代碼(例如,如果我們有EN +日本+ CH語言代碼,關鍵只值EN + CH,該列表將包含JP):

public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes() 
    { 
     var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive(); 
     var languageIdList = languageList.Select(q => q.Id); 
     var languageIdDictionary = languageList.ToDictionary(q => q.Id); 

     var keyList = this.GetActive() 
      .Select(q => q.Key) 
      .Distinct(); 

     var result = new List<LocalizationKeyWithMissingCodes>(); 
     foreach (var key in keyList) 
     { 
      // Get missing codes 
      var existingCodes = this.Get(q => q.Active && q.Key == key) 
       .Select(q => q.LanguageId); 

      // ToList to make sure it is processed at application 
      var missingLangId = languageList.Where(q => !existingCodes.Contains(q.Id)) 
       .ToList(); 
      result.Add(new LocalizationKeyWithMissingCodes() 
      { 
       Key = key, 
       MissingCodes = missingLangId 
        .Select(q => languageIdDictionary[q.Id].Code), 
      }); 
     } 

     result = result.OrderByDescending(q => q.MissingCodes.Count() > 0) 
      .ThenBy(q => q.Key) 
      .ToList(); 

     return result; 
    } 

我覺得我目前的解決辦法是不好的,因爲它使查詢調用每個鍵。有沒有辦法通過使其更快或者在一個查詢調用中打包來改進它?

編輯:這是答案的最終查詢:

public IQueryable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes() 
    { 
     var languageList = Utils.ResolveDependency<ILanguageRepository>().GetActive(); 
     var localizationList = this.GetActive(); 

     return localizationList 
      .GroupBy(q => q.Key, (key, items) => new LocalizationKeyWithMissingCodes() 
      { 
       Key = key, 
       MissingCodes = languageList 
        .GroupJoin(
         items, 
         lang => lang.Id, 
         loc => loc.LanguageId, 
         (lang, loc) => loc.Any() ? null : lang) 
        .Where(q => q != null) 
        .Select(q => q.Code) 
      }).OrderByDescending(q => q.MissingCodes.Count() > 0) // Show the missing keys on the top 
      .ThenBy(q => q.Key); 
    } 

回答

3

另一種可能性,使用LINQ:

public IEnumerable<LocalizationKeyWithMissingCodes> GetAllKeysWithMissingCodes(
    List<Language> languages, 
    List<Localization> localizations) 
{ 
    return localizations 
     .GroupBy(x => x.Key, (key, items) => new LocalizationKeyWithMissingCodes 
     { 
      Key = key, 
      MissingCodes = languages 
       .GroupJoin(// check if there is one or more match for each language 
        items, 
        x => x.Id, 
        y => y.LanguageId, 
        (x, ys) => ys.Any() ? null : x) 
       .Where(x => x != null) // eliminate all languages with a match 
       .Select(x => x.Code) // grab the code 
     }) 
     .Where(x => x.MissingCodes.Any()); // eliminate all complete keys 
} 
+0

這太神奇了!我永遠無法想到它。 –

+1

參加實踐:) – devuxer

2

這裏是SQL邏輯,以確定失蹤「完整」的語言分配的按鍵:

SELECT 
    all.[Key], 
    all.LanguageId 
FROM 
    (
    SELECT 
     loc.[Key], 
     lang.LanguageId 
    FROM 
     Language lang 
    FULL OUTER JOIN 
     Localization loc 
     ON (1 = 1) 
    WHERE 
     lang.Active = 1 
    ) all 
LEFT JOIN 
    Localization loc 
    ON (loc.[Key] = all.[Key]) 
    AND (loc.LanguageId = all.LanguageId) 
WHERE 
    loc.[Key] IS NULL; 

要查看所有鍵(而不是過濾):

SELECT 
    all.[Key], 
    all.LanguageId, 
    CASE WHEN loc.[Key] IS NULL THEN 1 ELSE 0 END AS Flagged 
FROM 
    (
    SELECT 
     loc.[Key], 
     lang.LanguageId 
    FROM 
     Language lang 
    FULL OUTER JOIN 
     Localization loc 
     ON (1 = 1) 
    WHERE 
     lang.Active = 1 
    ) all 
LEFT JOIN 
    Localization loc 
    ON (loc.[Key] = all.[Key]) 
    AND (loc.LanguageId = all.LanguageId); 
+0

這確實是得到真正的鍵(有一個錯字,它是'lang.Id'),但我不知道爲什麼我得到重複記錄在同'Key'和'LanguageId'(我想與Active = 0記錄的問題),althought我說你的病情爲'WHERE lang.Active = 1 AND loc.Active = 1'。 –

+0

當然,我不知道你的表邏輯的細節,但總體來說上述應該工作。它是否適用於您的編輯? – Nicarus

+0

是的,刪除重複,它似乎工作,我會檢查並嘗試將其轉換爲LINQ查詢並告訴你結果。另外,我的要求是返回每個鍵,不管它們是否缺少值。 –

1

你的代碼似乎在做很多數據庫查詢y和物化..

在LINQ而言,單個查詢應該是這樣..

我們把語言和本地化表的笛卡爾乘積得到的(關鍵代碼)的所有組合,然後減去關係中存在的(鍵,代碼)元組。這給了我們不存在的(密鑰,代碼)組合。

var result = context.Languages.Join(context.Localizations, lang => true, 
loc => true, (lang, loc) => new { Key = loc.Key, Code = lang.Code }) 
.Except(context.Languages.Join(context.Localizations, lang => lang.Id, 
loc => loc.LanguageId, (lang, loc) => new { Key = loc.Key, Code = lang.Code })) 
.GroupBy(r => r.Key).Select(r => new LocalizationKeyWithMissingCodes 
{ 
Key = r.Key, 
MissingCodes = r.Select(kc => kc.Code).ToList() 
}) 
.ToList() 
.OrderByDescending(lkmc => lkmc.MissingCodes.Count()) 
.ThenBy(lkmc => lkmc.Key).ToList(); 

p.s.我在旅途中輸入了這個LINQ查詢,所以讓我知道它是否有語法問題。 查詢的要點是我們採取笛卡爾乘積並減去匹配的行。

+0

這是我第一次見到LINQ'Include'。它是什麼?我無法通過它,因爲'l => l.Localization'不存在,只有'l.Localizations'等下一個Select語句,我無法選擇'Key',因爲'l.Localizations '是一個集合。 –

+0

我應該使用第二個連接作爲「Except」的參數嗎? –

+0

是的..我更新了答案,只加入了第二個結果集的語言ID .. –