不加载基础内存使用LinqToSql

3

我有一个问题,如果我使用LinqToSql,我的程序会将数据库加载到内存中。

举个例子:

//pageNumber = 1; pageSize = 100;                
                var result =
                    (
                    from a in db.Stats.AsEnumerable()
                    where (DictionaryFilter(a, sourceDictionary) && DateFilter(a, beginTime, endTime) && ErrorFilter(a, WarnLevel))
                    select a
                    );
                var size = result.Count(); // size = 1007
                var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
                return resultList;

DictionaryFilter、DateFilter 和 ErrorFilter 是用于过滤我的数据库的函数。使用这些函数后,我的程序会占用约250MB的内存。

如果我不使用这些函数:

var size = result.Count(); 

我的程序使用了大约120MB的内存。 在使用这段代码之前,我的程序只使用了大约35MB的内存。

如何使用count和take函数而不会将整个数据库加载到内存中?

static bool DateFilter(Stat table, DateTime begin, DateTime end)
{

    if ((table.RecordTime >= begin.ToFileTime()) && (table.RecordTime <= end.ToFileTime()))
    {
        return true;
    }
    return false;
}
static bool ErrorFilter(Stat table, bool[] WarnLevel)
{
    if (WarnLevel[table.WarnLevel]) return true;
    else return false;
}
static bool DictionaryFilter(Stat table, Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
    foreach (var word in sourceDictionary)
    {
        if (table.SourceName == word.Key.SourceName)
        {
            return word.Value;
        }
    }
    //
    return false;
}
2个回答

9
简单来说,不要使用.AsEnumerable()。这意味着“切换到LINQ-to-Objects”。在此之前,db.StatsIQueryable<T>,这是一个可组合的API,并且会按照您的预期执行操作。 然而,这意味着您不能使用C#方法,如DictionaryFilterDateFilter,而必须以Expression API为基础进行组合。如果您能说明它们的作用,我可能可以进一步提供建议。 通过您的编辑,过滤可以进行微调,例如:
static IQueryable<Stat> ErrorFilter(IQueryable<Stat> source, bool[] WarnLevel) {
    // extract the enabled indices (match to values)
    int[] levels = WarnLevel.Select((val, index) => new { val, index })
                            .Where(pair => pair.val)
                            .Select(pair => pair.index).ToArray();

    switch(levels.Length)
    {
        case 0:
            return source.Where(x => false);
        case 1:
            int level = levels[0];
            return source.Where(x => x.WarnLevel == level);
        case 2:
            int level0 = levels[0], level1 = levels[1];
            return source.Where(
                  x => x.WarnLevel == level0 || x.WarnLevel == level1);
        default:
            return source.Where(x => levels.Contains(x.WarnLevel));
    }
}
日期过滤器更简单:
static IQueryable<Stat> DateFilter(IQueryable<Stat> source,
      DateTime begin, DateTime end)
{
    var from = begin.ToFileTime(), to = end.ToFileTime();
    return source.Where(table => table.RecordTime >= from
        && table.RecordTime <= to);
}

而字典有点像等级:

static IQueryable<Stat> DictionaryFilter(IQueryable<Stat> source,
    Dictionary<GetSourcesNameResult, bool> sourceDictionary)
{
    var words = (from word in sourceDictionary
                 where word.Value
                 select word.Key.SourceName).ToArray();

    switch (words.Length)
    {
        case 0:
            return source.Where(x => false);
        case 1:
            string word = words[0];
            return source.Where(x => x.SourceName == word);
        case 2:
            string word0 = words[0], word1 = words[1];
            return source.Where(
                  x => x.SourceName == word0 || x.SourceName == word1);
        default:
            return source.Where(x => words.Contains(x.SourceName));
    }
}

并且:

IQueryable<Stat> result = db.Stats;
result = ErrorFilter(result, WarnLevel);
result = DateFiter(result, beginTime, endTime);
result = DictionaryFilter(result, sourceDictionary);
// etc - note we're *composing* a filter here

var size = result.Count(); // size = 1007
var resultList = result.Skip((pageNumber-1)*pageSize).Take(pageSize).ToList();
return resultList;

关键是我们现在完全使用IQueryable<T>Expression


他可能不能简单地省略 AsEnumerable。看一下他的过滤器,它似乎由一堆自定义的内存谓词组成。他必须将所有过滤子句转换为 Expression。编辑:没错。 - Wiktor Zychla
我在我的帖子中添加了过滤器功能。 - Alexander Mashin
@EXTRAM 我添加了 ErrorFilter 的实现等。 - Marc Gravell

1

如果我不使用AsEnumerable(),我能否使用我的C#函数? - Alexander Mashin
是的,如果您根据IQueryable接口编写它们的代码。看起来Marc的答案比我快 :) - Eric Eijkelenboom

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,