5

いくつかのパラメータに基づいて、文字列のリストを指定して2つの日付間の「トランザクション」を見つけるメソッドがあります。リストが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;
        }
    }

これがスタックトレースです...VisualStudioが処理できるポイントを超えています。

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    

私の質問は、スタックオーバーフローの例外について心配する必要がないように、このクエリを処理する方法は何ですか?

4

3 に答える 3

2

大きなコレクションを反復処理することでスタックを吹き飛ばしているように見えますが、同時にそれらのオブジェクトをリストに追加すると、2つの大きな、しかし基本的に同一のコレクションになります。代わりに、任意のIEnumerableを受け入れるリストにAddRangeを使用してください。

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>();
    }
}
于 2013-02-21T20:28:15.687 に答える
1

ここには2つの大きな問題があります。一意の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()、このクエリに適用できます。ただし、匿名オブジェクトを作成してローカルリストにコピーする必要はありません。

于 2013-02-21T20:50:34.363 に答える
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句で使用されているリストにはある種の制限があると思いますが、ユーザーコードを制限し、単純なフィルターを実行して関連するコードリストにあるものだけを取得するため、これがより良い解決策になりました。 。

于 2013-02-21T22:41:16.687 に答える