2016-04-15 15 views
0

エンティティフレームワークをよりよく理解し、データベースを照会するためのバックエンドデバイスとして効果的に使用できるようにするため、Entity Frameworkをテストしました。エンティティフレームワークのパフォーマンスを向上させるクエリ

参考までに、Entity Frameworkはデフォルトで遅延読み込みを使用しています。私が作成しようとしているようなバックエンドシステムの場合、これは有用ではありません。

int x = 0; 
using (SandboxContext dbc = new SandboxContext()) { 
    var customers = (from c in dbc.Customer orderby c.AcctNumber select new { c.CustomerKey, c.AcctNumber }).ToList(); 
    var products = (from p in dbc.Product orderby p.CustomerKey select new { p.CustomerKey }).ToList(); 
    foreach (var c in customers) 
     foreach (var p in products.Where(s => s.CustomerKey == c.CustomerKey)) 
      ++x; 
    dbc.Dispose(); 
} 
return x; 

これは現在使用しているコードと同じです。

私が試したことはすべて、このメソッドのパフォーマンスを悪化させるように思えます。参考までに、このコードはマシン上で約5秒間実行され、約22000個の自動生成データが返されます。このコードは、他の一方で、同じ結果をほぼ瞬時に実行されます。

SqlConnection sqlc = new SqlConnection(sqlConnectString); 
SqlDataAdapter sqlda = new SqlDataAdapter("SELECT customerkey, acctnumber FROM customers", sqlc); 

DataTable dtCustomers = new DataTable(), dtProducts = new DataTable(); 
sqlda.Fill(dtCustomers); 
sqlda.SelectCommand.CommandText = "SELECT customerkey FROM product"; 
sqlda.Fill(dtProducts); 
sqlda.Dispose(); 
sqlc.Close(); 

DataView dvCustomers = new DataView(dtCustomers) { Sort = "AcctNumber" }; 
DataView dvProducts = new DataView(dtProducts) { Sort = "CustomerKey" }; 

int x = 0; 
for (int y = 0; y < 1000; y++) 
    foreach (DataRowView drvCustomers in dvCustomers) { 
     DataRowView[] drvaProducts = dvProducts.FindRows(drvCustomers["customerkey"].ToString()); 
     foreach (DataRowView drvProducts in drvaProducts) 
      ++x; 
     } 
return x; 

は、私がこれまでEntity Frameworkのコードの清潔さと読みやすさを好むが、私はかなりだ情報の一部の重要な部分が欠けてると思います私の方法のスピードを傷つける。少なくとも、DataTable/DataView/DataRowView実装の速度に近づくようにEntity Frameworkコードを改善する考えはありますか?

+0

? – dotctor

+3

モデルに必要なすべての往復プロパティがある場合(つまり、 'Customer'に' Product'のコレクションがあります)、あなたがしようとしているのは 'dbc.Customer.SelectMany(c => c。製品).Count() '。あなたの 'Customer'が' Product'のコレクションを持っていない場合は、これを設定することを検討してください。これがなければ、EFの良さを逃してしまうでしょう。 – spender

+1

最初に、SQLを構築するために使用されるモデルを構築するEFのウォームアップコストがあります。 https://msdn.microsoft.com/en-us/library/bb896240(v=vs.100).aspx次に、データベースを2回ヒットします。それを防ぐために将来のクエリを使用することができます。 https://lostechies.com/jimmybogard/2014/03/11/efficient-querying-with-linq-automapper-and-future-queries/ –

答えて

3

コンテキストが正しく設定されている場合、CustomerにはProductのコレクションが含まれているはずです。この答えのために、そのプロパティをProductsと呼ぶことにしましょう。

Productsプロパティを使用すると、EFに代わって参加を依頼するので、自分で明示的に参加を書く必要がなくなります。実際、EFがそれをあなたのためにすべて行うということを考えると、「長持ち」に参加することは不必要に冗長であり、非常に奇妙なことになります。効果的にあなたの製品のリストのリストを提供します

dbc.Customer.Select(c => c.Products) 

だから、今、あなたは同じように簡単に顧客に属している製品を選択することができます。

ここでリストのリストをSelectManyのリストにフラット化すると、その結果の商品リストを簡単にカウントできます。

ので:べき 'X'の値が何を表しているか

using(var dbc = new SandboxContext()) 
{ 
    var customerProductsCount = dbc.Customer 
            .SelectMany(c => c.Products) 
            .Count(); 
} //note, no .Dispose... `using` took care of that for us. 
1

using文で文脈を破棄しないでください。usingがそれを行います。

ToListを呼び出すと、クエリが実行され、複雑なクエリを作成してデータベース側でチューニングすることができなくなります。 ToListを呼び出すと、データベースからデータがフェッチされ、パフォーマンスが大幅に低下する可能性があります。

不要なクエリの結果を注文する必要はありません。それは単にオーバーヘッドを追加し、実行時間を増加させます。

最後に、コード全体を単純なクエリ(thanks JoaoFSA)に減らすことができるようです。あなたがDB上で発注を行っているが、私は右のそれを読んでいる場合は、現在使用しているものの中にあなたがコードでそれを行うEFを使用しているとき

using (SandboxContext dbc = new SandboxContext()) 
{ 
    return dbc.Customer.Join(dbc.Product, 
       c => c.CustomerKey, 
       p => p.CustomerKey, 
       (c, p) => new { Customer = c, Product = p}) 
       .Count(); 
} 
1

はまあ、パフォーマンスが異なる場合があります。

しかし、私があなたのアプローチで見つけた最大の問題は、DBからアプリケーションへのすべての顧客と製品をロードしてから、アプリケーションで結合とカウントを行うことです。

using (SandboxContext dbc = new SandboxContext()) { 
    return (from c in dbc.Customer join p in dbc.Product on c.CustomerKey equals p.CustomerKey select p).Count(); } 
+0

fyiとして(すべてのバージョンに対応しているかどうかわかりませんが、構文は比較的新しいです)、クエリブロックの最後にselectまたはgroup節がない限り、指定はコンパイルされません一度それがそこにある、非常によく)。 – LightToTheEnd

+0

うん、私はその場でそれを書いて、それを忘れてしまった。追加された – JoaoFSA

1

遅延読み込みを無効にするにはどうしますか?これを無効にするには、SandboxContextコンテキストのコンストラクタ内に

this.Configuration.LazyLoadingEnabled = false; 

を追加して無効にします。

関連する問題