2011-06-27 25 views
5

Tôi đang gặp một số vấn đề với truy vấn Sql do LINQ tạo ra, Vì môi trường của tôi khá lớn, tôi đã làm một ví dụ đơn giản phản ánh vấn đề.Tối ưu hóa SQL được tạo bởi LINQ Query trong Entity Framework 4.1 với các liên kết một-nhiều

Đây là mô hình của tôi:

public class ClassA 
{ 
    public int ID { get; set; } 
    public virtual ICollection<ClassB> Children { get; set; } 
} 

public class ClassB 
{ 
    public int ID { get; set; } 
    public string Data { get; set; } 
} 

public class ClassC 
{ 
    public int ID { get; set; } 
    public virtual ICollection<ClassB> Children { get; set; } 
} 

Rất đơn giản huh?

Vâng, đây là câu hỏi của tôi:

var classA = (from x in db.ClassAs 
      where x.ID == 2 
      select x).First(); 
var classesB = (from b in classA.Children 
       select b.Data).Skip(10).Take(10); 

classesB.ToList(); 

Vấn đề là khi truy vấn này được dịch sang SQL:

(from x in db.ClassAs 
where x.ID == 2 
select x).First() 

trở thành:

SELECT TOP (1) 
[Extent1].[ID] AS [ID] 
FROM [dbo].[ClassAs] AS [Extent1] 
WHERE 2 = [Extent1].[ID] 

và:

from b in classA.Children 
select b.Data).Skip(10).Take(10) 

trở thành:

SELECT 
[Extent1].[ID] AS [ID], 
[Extent1].[Data] AS [Data], 
[Extent1].[ClassA_ID] AS [ClassA_ID] 
FROM [dbo].[ClassBs] AS [Extent1] 
WHERE ([Extent1].[ClassA_ID] IS NOT NULL) AND ([Extent1].[ClassA_ID] = @EntityKeyValue1) 

Tôi mong rằng truy vấn được tạo ra sẽ là một cái gì đó như thế này:

SELECT [Data] AS [Data] 
FROM (SELECT 
     [Data] AS [Data], 
     rownum = ROW_NUMBER() OVER (ORDER BY [B].[ID]) 
     FROM ClassBs AS B , ClassAs AS A 
     WHERE B.ClassA_ID = A.ID 
     AND A.ID = 2) AS T1 
WHERE [t1].rownum BETWEEN 11 AND 20 
ORDER BY [t1].rownum 

Vấn đề lớn là Class A -> Class B đã luôn luôn nhiều hơn 10k Lines, và nó là, tất cả các dòng này đang được nạp vào bộ nhớ, và phân trang đang được thực hiện trong bộ nhớ, nhưng tôi ước rằng phân trang này sẽ được thực hiện bởi SQL Server.

Bất kỳ suy nghĩ nào về cách thực hiện điều này?

Trả lời

2

Bạn phải khác nhau giữa linq-to-entity và linq-to-objects. Điều này:

var classA = (from x in db.ClassAs 
       where x.ID == 2 
       select x).First(); 

là linq-to-entity. Bạn đang truy cập db.ClassAs cung cấp IQueryable để tạo cây biểu thức sẽ được thực thi dưới dạng SQL trong cơ sở dữ liệu khi bạn gọi First(). Nhưng điều này:

var classesB = (from b in classA.Children 
       select b.Data).Skip(10).Take(10); 

là hình thức đối tượng. Bản thân truy vấn được xác định trên bộ sưu tập (HashSet) và được thực thi trên bộ sưu tập đó. Vì tài sản của bạn được đánh dấu là virtual EF sẽ kích hoạt tải chậm cho bạn và điền vào tất cả dữ liệu trong thuộc tính đó để cho phép bạn thực hiện điều đó trong truy vấn bộ nhớ. Nó sẽ không bao giờ hoạt động theo cách khác.

Nếu bạn muốn chắc truy vấn cơ sở dữ liệu cho tài sản có liên quan của bạn, bạn phải sử dụng tải rõ ràng thay vì tải lười biếng:

db.Entry(classA) 
    .Collection(c => c.Children) 
    .Query() 
    .OrderBy(...) // You must order entities before you can use Skip and Take 
    .Skip(10) 
    .Take(10) 
    .Load(); 

var classesB = classA.Children; 
+0

cảm ơn! Nó đã làm việc! –

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