我有一个巨大的字符串列表,我想比较数据库表的记录。什么是最好的解决方案?
您可以假设指定表的名称为ATable
,其结构如下:
public class ATable
{
[Key]
public long Id{get;set;}
[Key]
public long Name{get;set;}
}
我写了以下代码
using(var context = new MyDbContext())
{
context.Log = (log) => {Console.WriteLine(log)};
var result = context.ATables.Where(item => hugeList.Contains(item.Name)).ToList();
}
我检查了生成的日志,我看到上面的代码已经转换为SQL IN(...)
语句,并且由于列表应用程序崩溃的严重性。
我确信有一个很好的方法可以解决这个问题,那么专业人士可以向我展示正确的问题。
由于
答案 0 :(得分:2)
自EF 6 Alpha 1以来,EF包含一项改进,可加速Enumerable.Contains的转换。
如果您使用的是早期版本的EF或者列表的大小太大,那么建议@ Dan-o你可以用较小的块打破庞大的列表。为此,您可以使用@divega在此post中编写的解决方案。下面我将该解决方案应用于您的问题背景:
public partial class MyDbContext
{
public IEnumerable<ATable> GetElements(IEnumerable<long> hugeList, int chunkSize = 100)
{
foreach (var chunk in hugeList.Chunk(chunkSize))
{
var q = ATables.Where(a => chunk.Contains(a.Name));
foreach (var item in q)
{
yield return item;
}
}
}
}
切片可枚举序列的扩展方法:
public static class EnumerableSlicing
{
private class Status
{
public bool EndOfSequence;
}
private static IEnumerable<T> TakeOnEnumerator<T>(IEnumerator<T> enumerator, int count,
Status status)
{
while (--count > 0 && (enumerator.MoveNext() || !(status.EndOfSequence = true)))
{
yield return enumerator.Current;
}
}
public static IEnumerable<IEnumerable<T>> Chunk<T>(this IEnumerable<T> items, int chunkSize)
{
if (chunkSize < 1)
{
throw new ArgumentException("Chunks should not be smaller than 1 element");
}
var status = new Status { EndOfSequence = false };
using (var enumerator = items.GetEnumerator())
{
while (!status.EndOfSequence)
{
yield return TakeOnEnumerator(enumerator, chunkSize, status);
}
}
}
}
然后你可以这样做:
var result= context.GetElements(hugelist).ToList();