LINQ to SQL中的堆栈溢出异常

时间:2013-02-21 20:17:25

标签: c# entity-framework linq-to-sql stack-overflow

我有一个基于某些参数的方法,在给定字符串列表的两个日期之间找到“事务”。当列表是> 1000,我尝试迭代列表时出现堆栈溢出异常。

这是我的代码

public List<string> CodesWithTransactionsBetweenDates(DateTime startInclusive, DateTime endExclusive, List<string> associatedCodes, int marketId)
    {
        List<string> codesWithTransactionsInPeriod = new List<string>();
        using (var context = new MarketPlaceEntities())
        {
            var transactionList = (from transactions in context.Transactions
                                   where
                                    associatedCodes.Contains(transactions.User.Code) &&
                                    transactions.MarketId == marketId &&
                                    transactions.Date >= startInclusive &&
                                    transactions.Date < endExclusive
                                   group transactions by transactions.User.Code into uniqueIds
                                   select new { UserCode = uniqueIds.Key });
            foreach (var transaction in transactionList)
            {
                codesWithTransactionsInPeriod.Add(transaction.UserCode);
            }
            return codesWithTransactionsInPeriod;
        }
    }

这是堆栈跟踪......它超越了visual studio可以处理的点。

System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.VisitChildren(System.Data.Query.InternalTrees.Node n) + 0x3 bytes 
System.Data.Entity.dll!System.Data.Query.PlanCompiler.GroupAggregateRefComputingVisitor.VisitDefault(System.Data.Query.InternalTrees.Node n) + 0x2b bytes   
System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.VisitRelOpDefault(System.Data.Query.InternalTrees.RelOp op, System.Data.Query.InternalTrees.Node n) + 0xe bytes   
System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.VisitApplyOp(System.Data.Query.InternalTrees.ApplyBaseOp op, System.Data.Query.InternalTrees.Node n) + 0xe bytes  
System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.Visit(System.Data.Query.InternalTrees.OuterApplyOp op, System.Data.Query.InternalTrees.Node n) + 0xe bytes    
System.Data.Entity.dll!System.Data.Query.InternalTrees.OuterApplyOp.Accept(System.Data.Query.InternalTrees.BasicOpVisitor v, System.Data.Query.InternalTrees.Node n) + 0x10 bytes   
System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.VisitNode(System.Data.Query.InternalTrees.Node n) + 0x14 bytes    
System.Data.Entity.dll!System.Data.Query.InternalTrees.BasicOpVisitor.VisitChildren(System.Data.Query.InternalTrees.Node n) + 0x60 bytes    

我的问题是我可以处理此查询的方式是什么,以便我不必担心堆栈溢出异常?

3 个答案:

答案 0 :(得分:2)

看起来你正在通过迭代大型集合来吹嘘堆栈,但同时也将这些对象添加到列表中,从而产生两个大而基本相同的集合。相反,只需使用AddRange作为接受任何IEnumerable的列表。

List<string> codesWithTransactionsInPeriod = new List<string>();
using (var context = new MarketPlaceEntities())
    {
        return codesWithTransactionsInPeriod.AddRange((from transactions in context.Transactions
                               where
                                associatedCodes.Contains(transactions.User.Code) &&
                                transactions.MarketId == marketId &&
                                transactions.Date >= startInclusive &&
                                transactions.Date < endExclusive
                               group transactions by transactions.User.Code into uniqueIds
                               select uniqueIds.Key));
    }

或没有实例化空列表......

using (var context = new MarketPlaceEntities())
    {
        return (from transactions in context.Transactions
                               where
                                associatedCodes.Contains(transactions.User.Code) &&
                                transactions.MarketId == marketId &&
                                transactions.Date >= startInclusive &&
                                transactions.Date < endExclusive
                               group transactions by transactions.User.Code into uniqueIds
                               select uniqueIds.Key).ToList<string>();
    }

或保持懒惰......(编辑使用懒惰)

public Lazy<List<string>> LazyCodesWithTransactionsBetweenDates((DateTime startInclusive, DateTime endExclusive, List<string> associatedCodes, int marketId)
{
    return new Lazy<List<string>>(CodesWithTransactionsBetweenDates(startInclusive, endExclusive, associatedCodes, marketId));
}

private List<string> CodesWithTransactionsBetweenDates(DateTime startInclusive, DateTime endExclusive, List<string> associatedCodes, int marketId)
{
    using (var context = new MarketPlaceEntities())
    {
        return (from transactions in context.Transactions
                           where
                            associatedCodes.Contains(transactions.User.Code) &&
                            transactions.MarketId == marketId &&
                            transactions.Date >= startInclusive &&
                            transactions.Date < endExclusive
                           group transactions by transactions.User.Code into uniqueIds
                           select uniqueIds.Key).ToList<string>();
    }
}

答案 1 :(得分:0)

这里有两个大问题 - 对于每个唯一的id键,您在内存中使用单个属性创建新对象。您还有无用的本地列表,您可以在其中复制所有这些对象。每次填充列表的容量时,都会创建新的内部数组,并在那里复制所有对象。

您可以使用IEnumerable进行流处理。在这种情况下,您不需要将所有数据保存在内存中:

public IEnumerable<string> CodesWithTransactionsBetweenDates(
         DateTime startInclusive, DateTime endExclusive, 
         List<string> associatedCodes, int marketId)
{
    // do not use local list

    using (var context = new MarketPlaceEntities())
    {
        return from transactions in context.Transactions
                where associatedCodes.Contains(transactions.User.Code) &&
                      transactions.MarketId == marketId &&
                      transactions.Date >= startInclusive &&
                      transactions.Date < endExclusive
                      group transactions by transactions.User.Code into uniqueIds
                      select uniqueIds.Key; // do not create anonymous object
    }
}

如果您需要列表,可以对此查询应用ToList()。但是你绝对不需要创建匿名对象并将它们复制到本地列表。

答案 2 :(得分:0)

好的,经过一些试验和错误,并考虑了一些替代方案,我想出了一个似乎运作良好的解决方案。

public List<string> CodesWithTransactionsBetweenDates(DateTime startInclusive, DateTime endExclusive, List<string> associatedCodes, int marketId)
{
    using (var context = new MarketPlaceEntities())
    {
        var list = (from transactions in context.Transactions                            
                where
                    transactions.MarketId == marketId &&
                    transactions.Date >= startInclusive &&
                    transactions.Date < endExclusive                            
                select transactions.User.Code).Distinct().ToList<string>();

        return list.Where(c => associatedCodes.Contains(c)).ToList();
    }            
}

我想在where子句中使用了一个列表存在某种限制,这最终成为一个更好的解决方案因为我限制了用户代码,然后做了一个简单的过滤器来获取那些在相关代码清单。