2012-11-20 31 views
5

Tôi đã có Mô hình EF4 trong Ứng dụng .NET 4.0 mà tôi nâng cấp lên .NET 4.5 và EF5 (tham chiếu EntityFramework 5 Assembly mới), tôi đã thay đổi "Tạo mã Chiến lược "thành" Không "và thêm một mục tạo Mã (EF 5.x DbContext Generator) vào mô hình. Mà hoạt động tốt trong hầu hết mọi tình huống. Nhưng bây giờ có một vấn đề lớn khi tôi truy cập một tài sản chuyển hướng mà tài liệu tham khảo rất nhiều hồ sơ (> 100.000 hồ sơ). Cơ sở dữ liệu là một MSSQL 2005 Server.Giảm hiệu suất sau khi nâng cấp mô hình EF4 lên mô hình EF5 (DbContext)

kịch bản của tôi trông như thế này:

Mỗi khách hàng trong db của tôi có một ID duy nhất (Đó là khóa chính trong DB), ngoài mỗi hồ sơ khách hàng có chứa một ID khách hàng mẹ (trong trường hợp đặc biệt này hầu hết tham chiếu của khách hàng đến cùng một id mẹ (khoảng 145.000 bản ghi trong số 150.000 bản ghi) được ghi với id 1).

Mô hình của tôi chứa DbSet<CustomerBase> CustomerBase đại diện cho bảng chứa tất cả khách hàng dữ liệu của họ. Ngoài ra, còn có các thuộc tính điều hướng được gọi là ICollection<CustomerBase> CustomerBaseChildrenICollection<CustomerBase> CustomerBaseParent kết nối id khách hàng và id cha của khách hàng với độ lệch 0..1 đến *.

tôi xây dựng một phiên bản đơn giản để chứng minh những gì tôi có nghĩa là:

Xây dựng bảng với 150.000 hồ sơ cho thử nghiệm này:

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 

nhập bảng bao gồm các hạn chế referencial vào một mô hình tổ chức mới. Tôi đã sử dụng làm "Entity Container Name" ef5Entities. Sau đó, tôi đổi tên Navigation Propierties CustomerBase1 và CustomerBase2 thành CustomerBaseChildren và CustomerBaseParent.

Và đây là ứng dụng mẫu của tôi:

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

tôi đã sử dụng SQL Server Profiler để xem những gì khuôn khổ tổ chức thực hiện trên cơ sở dữ liệu. Có vẻ như rằng mã EF4 và EF5 là giống hệt nhau:

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 

Nếu tôi thực hiện tất cả ba tuyên bố trong Management Studio SQL, phải mất khoảng 1-2 giây cho đến khi tất cả 1 + 1 + 150,000 hồ sơ được lấy.

Nhưng khi tôi hiểu tuyên bố thứ ba là vấn đề. Nó trả về 150.000 bản ghi (ngay cả khi tôi sử dụng .First() như trong mã ở trên hoặc .Single() hoặc .Take(10) cho dù tôi có sử dụng .OrderBy(...) trước mặt hay không. Có vẻ như Entity Framework tìm nạp tất cả 150.000 bản ghi và lưu vào bộ nhớ cache trong DbContext rất nhiều thời gian (sau khi chờ đợi 2 phút, tôi đã dừng mã kiểm tra, kiểm tra nó với bảng cơ sở khách hàng thực sự của tôi mất 100 phút để hoàn thành) Caching trong ObjectContext chỉ mất khoảng 10 giây (điều đó là xấu khi xem xét rằng cơ sở dữ liệu chính nó là 5 -10 lần nhanh hơn, nhưng tôi có thể sống với điều đó).

Ngay cả mức tiêu thụ bộ nhớ một cách khủng khiếp, với ObjectContext ứng dụng Working Set tăng khoảng 200MB, với DbContext Set Working tăng cao hơn khoảng 10 lần.

Có cách nào để tiêm mệnh đề TOP (n) trong câu lệnh chọn để ngừng nhận tất cả các bản ghi từ cơ sở dữ liệu nếu tôi chỉ muốn bản ghi thứ nhất hoặc bản ghi đầu tiên (thường là 10 đến 100 bản ghi)? Trong tuyên bố đầu tiên có một TOP (1) trong câu lệnh chọn (hoặc TOP (2) nếu sử dụng .Single() thay vì .First()).

Tôi thậm chí đã cố gắng để thay đổi dòng CustomerBase someCustomer = context.CustomerBase.First(customer => customer.id == 1234); để không theo dõi: CustomerBase someCustomer = context.CustomerBase.AsNoTracking().First(customer => customer.id == 1234);

Nhưng sau đó một nhận được một System.InvalidOperationException tại CustomerBase firstChild = parentCustomer.CustomerBaseChildren.First(); với thông báo sau:

Khi một đối tượng được trả về với một lựa chọn NoTracking hợp nhất, Tải chỉ có thể được gọi khi EntityCollection hoặc EntityReference không chứa các đối tượng.

Nếu tôi thay đổi Chiến lược tạo mã trở lại để sử dụng ObjectContext với EF5, mọi thứ hoạt động tốt như trong EF4 cũ tốt. Tôi có làm điều gì sai khi sử dụng DbContext hoặc DbContext không thể sử dụng được trong môi trường lớn hơn không?

Trả lời

1

Gần đây tôi đã nâng cấp lên Visual Studio 2013, .NET 4.5.1 và Entity Framework 6. Nếu tôi sửa đổi mô hình của mình để sử dụng EF6 thay vì EF5, nó hoạt động như một nét duyên dáng.

Vì vậy, giải pháp là sử dụng EF4/EF5 với ObjectContext hoặc EF6 với DbContext.

Các vấn đề liên quan