2012-01-13 5 views
0

編集:テストの実行方法に欠陥があるため、テストを変更しました。リストの代わりにIEnumerableを使用すると、流暢なnhibernateが結果をキャッシュしないのです

最近、Fluent Nhibernateのパフォーマンスに関する問題を抱えていましたが、私は非常に奇妙だと思ったものを見つけました。 IEnumerableを作成したとき、Listのパフォーマンスが大幅に向上しました。私はなぜそれを理解しようとしていた。それはすべきではないように見え、Googleは何も変えなかった。私は同じコードを取り、ちょうどラインにsomeRepository.GetABunchOfSomethingtoを変更し、(.ToListをappened場合

//Class has various built in type fields, but no references to anything 
public class Something 
{ 
    public int ID; 
    public decimal Value; 
} 

var someRepository = new Repository(uow); 

//RUN 1 
var start = DateTime.Now; 
// Returns a IEnumerable from a session.Linq<SomeAgg> based on the passed in parameters, nothing fancy. Has about 1300 rows that get returned. 
var somethings = someRepository.GetABunchOfSomething(various, parameters); 
var returnValue = SumAllFunction(somethings); 
var timeSpent = DateTime.Now - start; //Takes {00:00:00.3580358} on my box 

//RUN2 
var start2 = DateTime.Now; 
var returnValue = someFunction(somethings); 
var timeSpent = DateTime.Now - start2; //Takes {00:00:00.0560000} on my box 



public decimal SumAllFunction(IEnumerable<Something> somethings) 
{ 
    return somethings.Sum(x => x.Value); //Value is a decimal that's part of the Something class 
} 

)今:

ここで私が走った基本的なテストです他

//RUN 1 
var start = DateTime.Now; 
var somethings = someRepository.GetABunchOfSomething(various, parameters).ToList(); 
var returnValue = SumAllFunction(somethings); 
var timeSpent = DateTime.Now - start; //Takes {00:00:00.3580358} on my box 

//RUN 2 
var start2 = DateTime.Now; 
var returnValue = SumAllFunction(somethings); 
var timeSpent = DateTime.Now - start2; //Takes {00:00:00.0010000} on my box 

何も変更します。これらの結果は非常に繰り返し可能です。だから、それはただの問題ではありません。

TLDRのバージョンはこれです:二回ループを通して同じIEnumerableをを実行している場合

セカンドランが長く、私は.ToListを(使用してリストへのIEnumerableを変更する場合よりも10〜20時間どこからでもかかります) 2つのループを実行する前に

私はSQLをチェックし、それが一覧だときに、SQLは一度だけ実行して、かなりの結果を得るために戻ってデータベースに移動することよりも、再びキャッシュされ、使用されるように表示されます。

IEnumerableの場合、IEnumerableの子にアクセスするたびに、それらを再水和するためにデータベースにアクセスします。

IEnumerableに追加/削除できないことを理解していますが、IEnumerableが最初にプロキシオブジェクトで満たされ、その後必要に応じてプロキシオブジェクトが後で水分補給されていることを理解しました。彼らが水和した後、再びDBに戻る必要はありませんが、それはそうではないようです。私は明らかにこれを回避しようとしていますが、それは奇妙だと思っていました。

+0

実行されるSQL文をプロファイルできますか? – empi

+0

'someFunction'は何をしますか?コードを表示できますか? (あなたの2つのコードを比較するのは簡単だと思いますか?もしあなたが "その行を....に置き換えたら"と書いていれば分かりやすいでしょうか?) –

+0

@ L.B。 someFunctionが表示され、コードの最後の3行です。 2番目のコードブロックを変更して、何が変更されたかをより正確に示すようにしました。 empi:私はSqlプロファイルを取得し、質問を更新しようとします。 – Zipper

答えて

3

GetABunchOfSomethingの結果でToList()を呼び出すと、その時点でクエリが実行され、結果が一覧に表示されます。 ToList()を呼び出さないと、someFunctionが実行されてからクエリが実行され、タイマーでは考慮されません。

私はあなたがその2つの間の時間差がそれに起因することがわかると思います。

更新

結果は、あなたに多分直感的ものの、理にかなっています。反復するまでクエリが実行されない理由と、結果がキャッシュされない理由が機能として提供されます。コードの2つの場所でリポジトリメソッドを呼び出すとします。 1回はFooでソートされ、別の時間はBarでフィルタリングされます。リポジトリメソッドがIQueryable<YourClass>を返した場合、そのオブジェクトに加えられた追加の変更は、コレクションがメモリ内で変更されるのではなく、実際に放出されるSQLに影響を与えます。その後

someRepository 
    .GetABunchOfSomething(various, parameters) 
    .ToList() 
    .Where(s => s.Bar == "SomeValue"); 

select * 
from someTable 
where Bar = 'SomeValue' 

をしかしあなたはこの代わりにやった場合、:あなたが反復一度生成されたSQLは次のようになります

someRepository 
    .GetABunchOfSomething(various, parameters) 
    .Where(s => s.Bar == "SomeValue"); 

:たとえば、あなたはこれを実行した場合代わりにテーブルからすべての行を取得し、アプリケーションが結果をフィルタリングします。

+0

この欠陥を指摘してくれてありがとう、根本的な問題は残っていますが、テストを修正すると、少なくともより良いデータを得てその違いを正しく表示できました。 – Zipper

+0

詳細については、私の更新を参照してください。 – Jacob

+0

これははるかに理にかなっています。どうもありがとうございました。 – Zipper

関連する問題