2009-11-17 25 views
5

Tôi gặp sự cố khi xây dựng truy vấn LINQ của khung thực thể có mệnh đề chọn chứa các cuộc gọi phương thức tới các đối tượng không phải EF.Điều khoản chọn có chứa các cuộc gọi phương thức không phải EF

Mã bên dưới là một phần của ứng dụng được sử dụng để chuyển đổi dữ liệu từ một DBMS thành một giản đồ khác trên một DBMS khác. Trong đoạn mã dưới đây, vai trò là lớp tùy chỉnh của tôi không liên quan đến các DBMS, và các lớp khác đều được tạo ra bởi Entity Framework từ giản đồ DB của tôi:

// set up ObjectContext's for Old and new DB schemas 
var New = new NewModel.NewEntities(); 
var Old = new OldModel.OldEntities(); 

// cache all Role names and IDs in the new-schema roles table into a dictionary 
var newRoles = New.roles.ToDictionary(row => row.rolename, row => row.roleid); 

// create a list or Role objects where Name is name in the old DB, while 
// ID is the ID corresponding to that name in the new DB 
var roles = from rl in Old.userrolelinks 
      join r in Old.roles on rl.RoleID equals r.RoleID 
      where rl.UserID == userId 
      select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] }; 
var list = roles.ToList(); 

Nhưng gọi ToList mang lại cho tôi NotSupportedException này:

LINQ to Entities không nhận phương pháp 'Int32 get_Item (System.String)' phương pháp, và phương pháp này không thể được dịch ra tiếng một biểu hiện cửa hàng

Các âm thanh như LINQ-to-Entities đang chặn trên cuộc gọi của tôi để kéo giá trị ra khỏi từ điển được đặt làm tên khóa. Tôi thừa nhận là không hiểu đủ về EF để biết tại sao đây lại là một vấn đề.

Tôi đang sử dụng nhà cung cấp khung thực thể dotConnect for PostgreSQL của devart, mặc dù tôi giả định vào thời điểm này rằng đây không phải là vấn đề cụ thể về DBMS.

Tôi biết tôi có thể làm cho nó hoạt động bằng cách chia tay truy vấn của tôi vào hai truy vấn, như thế này:

var roles = from rl in Old.userrolelinks 
      join r in Old.roles on rl.RoleID equals r.RoleID 
      where rl.UserID == userId 
      select r; 
var roles2 = from r in roles.AsEnumerable() 
      select new Role { Name = r.RoleName, ID = newRoles[r.RoleName] }; 
var list = roles2.ToList(); 

Nhưng tôi đã tự hỏi nếu có một cách thanh lịch hơn và/hoặc hiệu quả hơn để giải quyết vấn đề này , lý tưởng mà không chia nó thành hai truy vấn.

Dù sao, câu hỏi của tôi là hai phần:

Trước tiên, tôi có thể chuyển đổi truy vấn LINQ này thành một cái gì đó Entity Framework sẽ chấp nhận, lý tưởng mà không cần tách thành hai mảnh?

Thứ hai, tôi cũng muốn hiểu một chút về EF vì vậy tôi có thể hiểu lý do tại sao EF không thể lớp mã .NET tùy chỉnh của tôi trên đầu trang của truy cập DB. DBMS của tôi không có ý tưởng làm thế nào để gọi một phương thức trên một lớp học từ điển, nhưng tại sao không thể EF chỉ đơn giản là thực hiện các cuộc gọi phương thức từ điển sau khi nó đã được kéo dữ liệu từ DB? Chắc chắn, nếu tôi muốn soạn nhiều truy vấn EF cùng nhau và đặt mã .NET tùy chỉnh ở giữa, tôi mong đợi điều đó sẽ thất bại, nhưng trong trường hợp này mã .NET chỉ ở cuối, vậy tại sao đây lại là vấn đề EF? Tôi cho rằng câu trả lời là một cái gì đó như "tính năng đó không biến nó thành EF 1.0" nhưng tôi đang tìm kiếm một chút giải thích thêm về lý do tại sao điều này là khó đủ để biện minh cho việc rời khỏi EF 1.0.

Trả lời

10

Vấn đề là khi sử dụng thực thi chậm trễ của LINQ, bạn thực sự phải quyết định nơi bạn muốn xử lý và dữ liệu nào bạn muốn đi qua đường ống đến ứng dụng khách của bạn. Trong trường hợp đầu tiên, LINQ giải quyết các biểu hiện và kéo tất cả các dữ liệu vai trò như một tiền thân của

New.roles.ToDictionary(row => row.rolename, row => row.roleid); 

Vào thời điểm đó, di chuyển dữ liệu từ DB vào khách hàng và được chuyển thành từ điển của bạn. Càng xa càng tốt.

Vấn đề là biểu hiện LINQ thứ hai của bạn đang yêu cầu các LINQ to làm biến đổi trên DB thứ hai sử dụng điển trên DB để làm như vậy.Nói cách khác, nó đang cố gắng tìm ra cách để chuyển toàn bộ cấu trúc từ điển sang DB để nó có thể chọn giá trị ID chính xác như là một phần của việc thực hiện truy vấn bị trì hoãn. Tôi nghi ngờ rằng nó sẽ giải quyết tốt nếu bạn thay đổi nửa thứ hai để

var roles = from rl in Old.userrolelinks 
      join r in Old.roles on rl.RoleID equals r.RoleID 
      where rl.UserID == userId 
      select r.RoleName; 
var list = roles.ToDictionary(roleName => roleName, newRoles[roleName]); 

Bằng cách đó, nó giải quyết chọn của bạn trên DB (chọn chỉ là rolename) như là một tiền thân của xử lý cuộc gọi ToDictionary (mà nó nên làm trên máy khách như bạn mong đợi). Đây là bản chất chính xác những gì bạn đang làm trong ví dụ thứ hai của bạn bởi vì AsEnumerable đang kéo dữ liệu đến máy khách trước khi sử dụng nó trong cuộc gọi ToList. Bạn có thể dễ dàng thay đổi nó thành một cái gì đó như

var roles = from rl in Old.userrolelinks 
      join r in Old.roles on rl.RoleID equals r.RoleID 
      where rl.UserID == userId 
      select r; 
var list = roles.AsEnumerable().Select(r => new Role { Name = r.RoleName, ID = newRoles[r.RoleName] }); 

và nó sẽ hoạt động như cũ. Các cuộc gọi đến AsEnumerable() giải quyết các truy vấn, kéo dữ liệu cho khách hàng để sử dụng trong Chọn theo sau nó.

Lưu ý rằng tôi chưa thử nghiệm điều này, nhưng theo như tôi hiểu Entity Framework, đó là giải thích tốt nhất của tôi cho những gì đang xảy ra dưới mui xe.

+0

Có vẻ như tôi đã đánh giá cao tính thông minh của LINQ đối với thực thể. Tôi đã giả định rằng L-to-E đủ thông minh để phân hủy một cây biểu thức thành phần mà DB có thể xử lý, và một phần khác mà nó phải giải quyết bằng cách sử dụng các cuộc gọi .NET (bên ngoài DB), và sau đó ghép hai cùng với nhau. Nếu tôi hiểu bạn một cách chính xác, bạn đang nói rằng L-to-E không phải là thông minh-- rằng nó chỉ đơn giản cố chuyển đổi mọi thứ * trong biểu thức thành SQL, và nếu có thứ gì đó không thể biến đổi (ví dụ: gọi một phương thức đối tượng .NET trong mệnh đề select), sau đó nó sẽ không thực hiện được? –

+1

Đúng vậy, Justin. Lưu ý: Tự động phân tách truy vấn trên các tầng, là 'truy vấn được phân phối', một vấn đề cực kỳ khó giải quyết chung. –

+0

Phải. LINQ to Entities giữ tất cả mọi thứ như một cây biểu thức mà cuối cùng nó sẽ sử dụng đối với cơ sở dữ liệu chỉ khi nó được * thực sự * lặp lại. Đó là "ngu ngốc" theo cách đó, nhưng đó là bởi vì, như Alex nói, truy vấn phân tán là một vấn đề cực kỳ khó giải quyết khi mọi thứ trở nên phức tạp hơn một chút. Tuy nhiên, hãy biết rằng bạn có thể sử dụng phép lặp làm dấu trang để kéo dữ liệu đến máy khách. –

3

Jacob hoàn toàn đúng. Bạn không thể chuyển đổi truy vấn mong muốn mà không tách nó thành hai phần, bởi vì Entity Framework không thể dịch lệnh get_Item thành truy vấn SQL.
Cách duy nhất là viết truy vấn LINQ to Entities và sau đó viết truy vấn LINQ to Objects vào kết quả của nó, giống như Jacob đã thông báo.
Vấn đề là thực thể cụ thể theo từng khuôn khổ, nó không phát sinh từ việc chúng tôi triển khai hỗ trợ Khung thực thể.

+0

+1. cảm ơn vì phản ứng nhanh - tốt để xem các nhà cung cấp nhảy vào để trả lời các câu hỏi liên quan đến mã của họ! –

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