Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stack Overflow Exception in LINQ to SQL

I have a method that based on some parameters finds 'Transactions' between two dates given a list of strings. When the list is > 1000, I get a stack overflow exception trying to iterate on the list.

Here is my code

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;
        }
    }

Here is the stack trace... it goes past the point which visual studio can handle.

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    

My question is what is a way I can handle this query so that I do not have to worry about a stack overflow exception?

like image 691
Kyle C Avatar asked Oct 21 '22 16:10

Kyle C


1 Answers

It seems like you're blowing the stack by iterating over the large collection, but also simultaneously add those objects to the list which results in two large, but basically identical collections. Instead just use AddRange for the list which accepts any 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));
    }

or without instantiating an empty 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).ToList<string>();
    }

or to preserve laziness... (Edited to use Lazy)

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>();
    }
}
like image 88
pwnyexpress Avatar answered Oct 24 '22 17:10

pwnyexpress