2015-12-10 36 views
0

我目前正在試圖從三個不同的表牽引結果,並將它們合併成一個列表,像這樣...LINQ的Concat的優化

var accountLogs = Context.AccountLogs.Where(s => s.Timestamp > fromDate).Select(s => new LogSummaryDto() 
{ 
    UserId = s.User.Id, 
    DisplayName = s.User.DisplayName 
}); 

var spellLogs = Context.SpellLog.Where(s => s.Timestamp > fromDate).Select(s => new LogSummaryDto() 
{ 
    UserId = s.User.Id, 
    DisplayName = s.User.DisplayName 
}); 
var generalLogs = Context.Log.Where(s => s.Timestamp > fromDate).Select(s => new LogSummaryDto() 
{ 
    UserId = s.User.Id, 
    DisplayName = s.User.DisplayName 
}); 

var m1 = accountLog.Concat(spellLogs); 
var m2 = m1.Concat(generalLogs).toList(); 

不過,我敢肯定,必須有使用LINQ to某種方式這是一個查詢,任何想法/指針?

+2

如果該查詢返回正確的結果,那很好,由於LINQ的延遲執行,您只執行一個查詢在這裏。其實你甚至沒有在你的代碼中執行它。使用'foreach'或用'm1.Concat(generalLogs).ToList();'創建一個集合。然後查詢將被執行。 –

+1

@ChrisF你可以做得更好:'m1 = accountLog.Concat(spellLog).Concat(generalLogs)',不使用'm2'! :)只是在開玩笑 –

+0

@TimSchmelter已經回答了這個問題,迄今爲止最快的解決方案就是它的原型。 –

回答

1

你可以使用Concat早,過濾所有一次,就像這樣:

var res = Context.AccountLogs.Select(s => new { 
     s.TimeStamp 
    , s.User.Id 
    , s.User.DisplayName 
    }).Concat(Context.SpellLog.Select(s => new { 
     s.TimeStamp 
    , s.User.Id 
    , s.User.DisplayName 
    })).Concat(Context.Log.Select(s => new { 
     s.TimeStamp 
    , s.User.Id 
    , s.User.DisplayName 
    })) 
    // Selects/Concats above provide uniform structure 
    // on which to apply the common Where clause 
    .Where(s => s.TimeStamp > fromDate) 
    .Select(s => new LogSummaryDto() { 
     UserId = s.User.Id 
    , DisplayName = s.User.DisplayName 
    }); 
+0

這會比OP的方法更有效嗎?因爲它的可再生性和可調試性較差 –

+1

@TimSchmelter這種方法更易於維護,因爲where子句不會重複。 「選擇」按照與原始文件相同的方式重複,但在缺少通用基本類型的情況下很難修復。就效率而言,我懷疑它會大致相同,因爲往返次數不會改變。 – dasblinkenlight

+0

我會給出一個結論,看看有什麼不同,@TimSchmelter被授予延遲執行等待,直到它被拉到.toL​​ist()等,但有時寫入查詢的莊園會影響處理時間,因爲我發現與.Group有一天。然而,這比前幾天我所做的要簡單得多,現在我可能只是在想。 –

-1

您可以使用union:

var logs = ((
from s in Context.AccountLogs 
where s.TimeStamp > fromDate 
select new { 
UserId = s.User.Id, 
DisplayName = s.User.DisplayName 
}) 
.Union(
from s in Context.SpellLog 
where s.TimeStamp > fromDate 
select new { 
UserId = s.User.Id, 
DisplayName = s.User.DisplayName 
}) 
.Union(
from s in Context.Log 
where s.TimeStamp > fromDate 
select new 
{ 
UserId = s.User.Id, 
DisplayName = s.User.DisplayName 
})).ToList(); 
1

你可以提取您的LINQ邏輯到自己的「LINQ操作」。這需要Context.AccountLogsContext.SpellLogContext.Log全部實現相同的接口。比方說,它們都實現ILog,如:

public interface ILog { 
    User User { get; } 
    DateTime Timestamp { get; } 
} 

然後,您可以實現自己的ExtractSummaryDtos這樣的:

public static class LogSummaryDtoExtensions { 
    public static IEnumerable<LogSummaryDto> ExtractSummaryDtos(this IEnumerable<ILog> logs, DateTime fromDate) { 
     return logs.Where(s => s.Timestamp > fromDate).Select(s => new LogSummaryDto { 
      UserId = s.User.Id, 
      DisplayName = s.User.DisplayName 
     }); 
    } 
} 

現在你可以建立這樣的列表:

var m3 = AccountLogs.ExtractSummaryDtos(fromDate) 
      .Concat(SpellLogs.ExtractSummaryDtos(fromDate)) 
      .Concat(Logs.ExtractSummaryDtos(fromDate)); 
+1

這只是讓它複雜化。 –

0
var m2 = new List<IEnumerable<dynamic>> {AccountLogs, SpellLogs, Logs} 
    .SelectMany(x => x.Where(y => y.Timestamp > fromDate).Select(s => new LogSummaryDto { 
     UserId = s.User.Id, 
     DisplayName = s.User.DisplayName 
    })); 
+0

我喜歡這個,因爲它乾淨,但它與其他解決方案一樣,連接發生在所有數據在應用時間戳過濾之前被拉的地方。 –

+0

在性能和簡潔代碼之間似乎存在權衡,這個答案很簡潔,許多其他代碼更高效。 – eoinmullan

+0

@ScottAlexander更新爲使用SelectMany。這應該在連接之前過濾。 – eoinmullan