2013-06-13 34 views
8

Tôi đang cố gắng tải danh sách các màu riêng biệt từ danh sách sản phẩm được tải trước đó trên một trang. Vì vậy, để kéo trong các sản phẩm tôi làm điều này:Khung thực thể AsNoTracking ngắt cuộc gọi đến Distinct

var products = Products 
    .Include(p => p.ProductColor) 
    .ToList(); 

Sau đó, tôi làm một số xử lý trên các sản phẩm chúng tôi muốn có được một danh sách của tất cả các màu sắc khác nhau được sử dụng bởi các sản phẩm, vì vậy tôi làm điều này:

var colors = products 
    .Select(p => p.ProductColor) 
    .Distinct(); 

Và điều này hoạt động tốt, tuy nhiên nếu tôi thêm một cuộc gọi đến .AsNoTracking() vào cuộc gọi sản phẩm ban đầu, bây giờ tôi nhận được một mục trong danh sách màu của tôi cho mỗi mục nhập trong danh sách sản phẩm.

Tại sao có sự khác biệt trong hai điều này? Có cách nào để giữ cho Entity Framework không theo dõi các đối tượng (chúng đang được sử dụng chỉ đọc) và để có được hành vi mong muốn không?

Đây là câu hỏi của tôi sau khi thêm các cuộc gọi đến AsNoTracking()

var products = Products 
    .AsNoTracking() 
    .Include(p => p.ProductColor) 
    .ToList(); 
+0

Từ những gì bạn đã gửi .AsNoTracking nên chỉ làm việc tốt, nơi chính xác là bạn đặt nó trong truy vấn của bạn –

+0

@LukeMcGregor, tôi đã cập nhật các câu hỏi với truy vấn của tôi với '.AsNoTracking' – heavyd

+0

là nó chỉ là một typo rằng truy vấn kết thúc bằng ToList và không có sự khác biệt trong nó? –

Trả lời

19

AsNoTracking "phá vỡ" DistinctAsNoTracking "phá vỡ" lập bản đồ sắc. Vì các thực thể được nạp với AsNoTracking() sẽ không được đính kèm vào bộ nhớ cache ngữ cảnh EF, thực hiện các thực thể mới cho mỗi hàng được trả về từ truy vấn trong khi khi theo dõi được bật, nó sẽ kiểm tra xem thực thể có cùng giá trị khóa đã tồn tại trong ngữ cảnh hay không , nó sẽ không tạo ra một đối tượng mới và chỉ sử dụng cá thể đối tượng đính kèm.

Ví dụ, nếu bạn có 2 sản phẩm và cả hai đều xanh:

  • Without AsNoTracking() truy vấn của bạn sẽ thành hiện thực 3 đối tượng: 2 Product đối tượng và 1 ProductColor đối tượng (Green). 1 sản phẩm có một tham chiếu đến màu xanh lá cây (trong ProductColor tài sản) và sản phẩm 2 có một tham chiếu với trường hợp đối tượng cùng Green, tức là

    object.ReferenceEquals(product1.ProductColor, product2.ProductColor) == true 
    
  • Với AsNoTracking() truy vấn của bạn sẽ thành hiện thực 4 đối tượng: 2 đối tượng sản phẩm và 2 các đối tượng màu (cả hai đều đại diện cho Green và có cùng giá trị khóa). 1 sản phẩm có một tham chiếu đến màu xanh lá cây (trong ProductColor tài sản) và sản phẩm 2 có một tham chiếu đến màu xanh lá cây nhưng đây là một đối tượng dụ, tức là

    object.ReferenceEquals(product1.ProductColor, product2.ProductColor) == false 
    

Bây giờ, nếu bạn gọi Distinct() trên một bộ sưu tập bộ nhớ (LINQ-to-Objects) so sánh mặc định cho Distinct() không có tham số là so sánh các định danh tham chiếu đối tượng. Vì vậy, trong trường hợp 1 bạn chỉ nhận được 1 đối tượng màu xanh lá cây, nhưng trong trường hợp 2 bạn sẽ nhận được 2 đối tượng màu xanh lá cây.

Để nhận kết quả mong muốn sau khi bạn đã chạy truy vấn với AsNoTracking(), bạn cần so sánh bằng khóa thực thể. Bạn có thể sử dụng quá tải thứ hai của Distinct, có tham số IEqualityComparer làm tham số. Ví dụ về việc triển khai thực hiện là here và bạn sẽ sử dụng thuộc tính khóa của ProductColor để so sánh hai đối tượng.

Hoặc - mà có vẻ dễ dàng hơn với tôi hơn IEqualityComparer thi tẻ nhạt - bạn viết lại Distinct() sử dụng một GroupBy (với ProductColor tài sản chính là chìa khóa nhóm):

var colors = products 
    .Select(p => p.ProductColor) 
    .GroupBy(pc => pc.ProductColorId) 
    .Select(g => g.First()); 

Các First() về cơ bản có nghĩa là bạn đang ném tất cả các bản sao đi và chỉ giữ đối tượng đối tượng đầu tiên cho mỗi giá trị khóa.

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