.NET 4.0とEF5(新しいEntityFramework 5アセンブリを参照)にアップグレードした.NET 4.0アプリケーションでEF4モデルを使用しましたが、ストラテジー "を"なし "に設定し、コード生成アイテム(EF 5.x DbContext Generator)をモデルに追加しました。ほとんどすべての状況でうまく動作します。しかし、多くのレコード(> 100.000レコード)を参照するナビゲーションプロパティにアクセスすると大きな問題が発生しました。データベースはMSSQL 2005 Serverです。ほぼすべてのEF4モデルをEF5モデル(DbContext)にアップグレードした後のパフォーマンス低下
私のデシベルのすべての顧客が、加えて、すべての顧客レコードが親の顧客IDをユニークなID(これは、DBの主キーです)が含まれています(この特殊なケースで:
私のシナリオでは、このようになります同じ親ID(150.000レコードのうち約145.000レコード)への顧客参照。これはid 1のレコードです)。
私のモデルには、すべての顧客とそのデータを含むテーブルを表すDbSet<CustomerBase> CustomerBase
が含まれています。さらに、顧客IDと顧客親IDを0..1〜*多重度で接続するICollection<CustomerBase> CustomerBaseChildren
およびICollection<CustomerBase> CustomerBaseParent
というナビゲーションプロパティがあります。
私は私が何を意味するかを証明するために単純化されたバージョンをビルド:
テーブルを作成し、このテストのための150.000レコードを持つ:
CREATE TABLE CustomerBase
(
id int IDENTITY(1,1) PRIMARY KEY NOT NULL,
parent_id int FOREIGN KEY REFERENCES CustomerBase(id),
some_data1 varchar(100),
some_data2 varchar(100),
some_data3 varchar(100),
some_data4 varchar(100),
some_data5 varchar(100),
)
GO
DECLARE @i int = 0
WHILE @i < 150000 BEGIN
INSERT INTO CustomerBase (parent_id, some_data1, some_data2, some_data3, some_data4, some_data5) VALUES (1, newid(), newid(), newid(), newid(), newid())
SET @i = @i + 1
END
は、新しいエンティティモデルにreferencial制約を含むテーブルをインポートします。私は "Entity Container Name" ef5Entitiesとして使用しました。次に、Navigation Propierties CustomerBase1とCustomerBase2の名前をCustomerBaseChildrenとCustomerBaseParentに変更しました。
static void Main(string[] args)
{
ef5Entities context = new ef5Entities();
// Start with selecting a single customer.
// Works fine in EF4/ObjectContext, works even faster in in EF5/DbContext
CustomerBase someCustomer = context.CustomerBase.First(customer => customer.id == 1234);
// Do something ...
// Get the parent of the customer.
// Works fine in EF4/ObjectContext, works even faster in in EF5/DbContext
CustomerBase parentCustomer = someCustomer.CustomerBaseParent;
// Do something ...
// Get the first child of the given parent id.
// Takes about 10 seconds in EF4/ObjectContext, I stopped the debugger after 2 minutes waiting in EF5/DbContext
CustomerBase firstChild = parentCustomer.CustomerBaseChildren.First();
Console.WriteLine("Press any key to quit.");
Console.ReadKey();
}
は私がデータベース上で実行されるどのようなエンティティフレームワークを参照するには、SQL Serverプロファイラを使用:
そして、ここでは私のサンプルアプリケーションです。 EF4とEF5コードはまったく同じであることを思わ:
SELECT TOP (1)
[Extent1].[id] AS [id],
[Extent1].[parent_id] AS [parent_id],
[Extent1].[some_data1] AS [some_data1],
[Extent1].[some_data2] AS [some_data2],
[Extent1].[some_data3] AS [some_data3],
[Extent1].[some_data4] AS [some_data4],
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE 1234 = [Extent1].[id]
exec sp_executesql N'SELECT
[Extent1].[id] AS [id],
[Extent1].[parent_id] AS [parent_id],
[Extent1].[some_data1] AS [some_data1],
[Extent1].[some_data2] AS [some_data2],
[Extent1].[some_data3] AS [some_data3],
[Extent1].[some_data4] AS [some_data4],
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE [Extent1].[id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
exec sp_executesql N'SELECT
[Extent1].[id] AS [id],
[Extent1].[parent_id] AS [parent_id],
[Extent1].[some_data1] AS [some_data1],
[Extent1].[some_data2] AS [some_data2],
[Extent1].[some_data3] AS [some_data3],
[Extent1].[some_data4] AS [some_data4],
[Extent1].[some_data5] AS [some_data5]
FROM [dbo].[CustomerBase] AS [Extent1]
WHERE [Extent1].[parent_id] = @EntityKeyValue1',N'@EntityKeyValue1 int',@EntityKeyValue1=1
私は、SQL Management Studioですべての3つの文を実行した場合、それはすべての1つの+ 1 + 150.000レコードがフェッチされるまで、約1〜2秒かかります。
しかし、私が理解しているように、3番目の声明が問題です。上記のコードで.Single()
または.Take(10)
のようなものを使用しても、150.000レコードが返されます。.OrderBy(...)
を使用するかどうかは関係ありません。Entity Frameworkが150.000レコードをすべて取得し、DbContextのレコードをキャッシュするのは恐ろしい(2分待ってから、私の実際の顧客ベーステーブルでそれをテストすると100分かかりました)ObjectContextのキャッシングには約10秒しかかかりません(これはデータベース自体が5 -10倍の速さが、私はそれで生活をできた)。
であってもメモリ消費がDbContextとのワーキングセットは200メガバイト程度上昇したアプリケーションは、ワーキングセットが約10倍上昇したObjectContextと、恐ろしくです。
SELECT文にTOP(n)句を挿入して、最初のレコードまたは最初のn個のレコード(通常10〜100個のレコード)のみが必要な場合は、データベースからすべてのレコードの受信を停止する方法はありますか?最初のステートメントでは、SELECTステートメントにTOP(1)(.First()
の代わりに.Single()
を使用する場合はTOP(2))がありました。
私も無追跡にラインCustomerBase someCustomer = context.CustomerBase.First(customer => customer.id == 1234);
を変更しようとしました:CustomerBase someCustomer = context.CustomerBase.AsNoTracking().First(customer => customer.id == 1234);
しかし、その後、次のメッセージを表示してCustomerBase firstChild = parentCustomer.CustomerBaseChildren.First();
でSystem.InvalidOperationException
を得る:
、 Loadは、EntityCollectionまたはEntityReferenceにオブジェクトが含まれていない場合にのみ呼び出すことができます。
コード生成戦略を変更して、EF5でObjectContextを使用すると、古いEF4のようにうまく動作します。私はDbContextを使用している間に何か間違っているのですか?それともDbContextは大規模な環境では使えないのですか?