2011-12-19 16 views
32

Tôi có một truy vấn L2E trả về một số dữ liệu có chứa các đối tượng trùng lặp. Tôi cần phải loại bỏ những đối tượng trùng lặp. Về cơ bản tôi nên giả định rằng nếu ID của họ là như nhau thì các đối tượng là trùng lặp. Tôi đã thử q.Distinct(), nhưng điều đó vẫn trả về các đối tượng trùng lặp. Sau đó, tôi đã thử thực hiện IEqualityComparer của riêng mình và chuyển nó tới phương thức Distinct(). Phương pháp này thất bại với văn bản sau đây:Cách triển khai IEqualityComparer để trả về các giá trị khác biệt?

LINQ to Entities không nhận ra phương pháp 'System.Linq.IQueryable 1[DAL.MyDOClass] Distinct[MyDOClass](System.Linq.IQueryable 1 [DAL.MyDOClass], System.Collections.Generic.IEqualityComparer`1 [DAL.MyDOClass ]) Phương pháp ' và phương pháp này không thể được dịch sang biểu thức cửa hàng.

Và đây là việc thực hiện các EqualityComparer:

internal class MyDOClassComparer: EqualityComparer<MyDOClass> 
    { 
     public override bool Equals(MyDOClass x, MyDOClass y) 
     { 
      return x.Id == y.Id; 
     } 

     public override int GetHashCode(MyDOClass obj) 
     { 
      return obj == null ? 0 : obj.Id; 
     } 
    } 

Vậy làm thế nào để tôi viết riêng tôi IEqualityComparer đúng cách?

Trả lời

86

Một EqualityComparer không phải là con đường để đi - nó chỉ có thể lọc kết quả của bạn thiết lập trong bộ nhớ ví dụ:

var objects = yourResults.ToEnumerable().Distinct(yourEqualityComparer); 

Bạn có thể sử dụng phương pháp GroupBy vào nhóm bằng ID và First phương pháp để chỉ cơ sở dữ liệu của bạn truy xuất mục nhập duy nhất cho mỗi ID ví dụ:

var objects = yourResults.GroupBy(o => o.Id).Select(g => g.First()); 
+8

1 Đây là một cuộc sống tiết kiệm, tuy nhiên lưu ý rằng bạn không thể sử dụng. Đầu tiên() thay vào đó bạn sẽ phải sử dụng .FirstOrDefault() –

+0

Tôi nợ bạn một nền giáo dục! Một trong những câu trả lời đó tôi ước tôi có thể đứng lên bỏ phiếu! – seebiscuit

+0

@yoelhalb không GroupBy đảm bảo không có nhóm nào được trả lại trống không? Không có cách nào cho một trong các nhóm được trả về trống, vì các nhóm được tạo thành bằng cách tách các phần tử – vijrox

7

Bạn sẽ không. Nhà điều hành Distinct được gọi trên cơ sở dữ liệu vì vậy bạn không thể sử dụng bất kỳ mã nào bạn viết trong ứng dụng của mình (bạn không thể di chuyển logic so sánh bình đẳng sang SQL) trừ khi bạn hài lòng với việc tải tất cả các giá trị không phân biệt và lọc riêng biệt trong ứng dụng của mình.

var query = (from x in context.EntitySet where ...).ToList() 
                .Distinct(yourComparer); 
+4

Tại sao' ToList() 'thay vì' ToEnumerable() '? –

+2

@Jon: Bạn nói đúng. 'ToEnumerable' sẽ là đủ. –

14

rich.okelly và Ladislav Mrnka đều chính xác theo nhiều cách khác nhau.

Cả hai câu trả lời của họ đối phó với thực tế là các phương pháp của IEqualityComparer<T> sẽ không được dịch sang SQL.

Tôi nghĩ rằng nó đáng xem xét những ưu và nhược điểm của từng loại, điều này sẽ mất nhiều hơn một bình luận.

phương pháp tiếp cận của người giàu viết lại truy vấn cho một truy vấn khác với cùng kết quả cuối cùng. Mã của họ sẽ dẫn đến nhiều hay ít cách bạn thực hiện điều này một cách hiệu quả bằng SQL được viết tay.

Ladislav kéo nó ra khỏi cơ sở dữ liệu tại điểm trước khi phân biệt, và sau đó một cách tiếp cận trong bộ nhớ sẽ hoạt động.

Vì cơ sở dữ liệu tuyệt vời khi thực hiện sắp xếp nhóm và lọc nội dung đa dạng, phụ thuộc vào khả năng của nó, nó có khả năng sẽ có hiệu suất cao nhất trong trường hợp này. Bạn có thể thấy rằng sự phức tạp của những gì đang xảy ra trước khi nhóm này là như vậy mà LINQ-to-thực thể không độc đáo tạo ra một truy vấn duy nhất mà là tạo ra một loạt các truy vấn và sau đó thực hiện một số công việc trong bộ nhớ, có thể khá khó chịu.

Nói chung nhóm thường đắt hơn các trường hợp trong bộ nhớ (đặc biệt nếu bạn mang nó vào bộ nhớ với AsList() thay vì AsEnumerable()).Vì vậy, nếu một trong hai bạn đã mang nó vào bộ nhớ ở giai đoạn này do một số yêu cầu khác, nó sẽ có hiệu suất cao hơn. Nó cũng sẽ là lựa chọn duy nhất nếu định nghĩa bình đẳng của bạn là cái gì đó không liên quan đến những gì có sẵn trong cơ sở dữ liệu, và tất nhiên nó cho phép bạn chuyển đổi định nghĩa bình đẳng nếu bạn muốn làm như vậy dựa trên một số IEqualityComparer<T> được truyền dưới dạng tham số. Trong tất cả, sự giàu có là câu trả lời tôi muốn nói sẽ là lựa chọn tốt nhất ở đây, nhưng những ưu và khuyết điểm khác nhau của Ladislav so với những người giàu có khiến nó cũng đáng được nghiên cứu và cân nhắc.

1

Cuối câu trả lời nhưng bạn có thể làm tốt hơn: nếu đối tượng Dal là một phần (thường được nếu nó là đối tượng DB), bạn có thể mở rộng nó như sau:

public partial class MyDOClass : IEquatable<MyDOClass> 
    { 

     public override int GetHashCode() 
     { 
      return Id == 0 ? 0 : Id; 
     } 

     public bool Equals(MyDOClass other) 
     { 
      return this.Id == other.Id; 
     } 
    } 

Và khác biệt sẽ hoạt động mà không bị quá tải.

Nếu không, bạn có thể tạo lớp IEqualityComparer như thế này:

internal class MyDOClassComparer : MyDOClass, IEquatable<MyDOClass>, IEqualityComparer<MyDOClass> 
    { 
     public override int GetHashCode() 
     { 
      return Id == 0 ? 0 : Id; 
     } 

     public bool Equals(MyDOClass other) 
     { 
      return this.Id == other.Id; 
     } 

     public bool Equals(MyDOClass x, MyDOClass y) 
     { 
      return x.Id == y.Id; 
     } 

     public int GetHashCode(MyDOClass obj) 
     { 
      return Id == 0 ? 0 : Id; 
     } 
    } 

Và một lần nữa, sử dụng riêng biệt mà không cần bất kỳ tình trạng quá tải