2015-12-10 31 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