2012-06-29 29 views
9

Có rất nhiều câu hỏi và câu trả lời và bài viết cho câu hỏi này có sẵn nhưng theo ý kiến ​​của tôi có vẻ là không thực sự rõ ràng/câu trả lời đúngEquals Làm thế nào nên chúng ta thực sự được implenting và GetHashCode cho các tổ chức NHibernate

Đối với tôi Ayende có tốt nhất việc thực hiện chung cho đến nay mà tôi đã nhìn thấy: http://ayende.com/blog/2500/generic-entity-equality

.... Nhưng đó là từ năm 2007 ....

đây có phải là 'cách tốt nhất' để thực hiện những phương pháp đặc biệt đối với với NHibernate 3.2 có chứa một số khác biệt trong proxy implemen tation cho các phiên bản trước đó?

+0

Vui lòng đánh dấu câu trả lời YES là câu trả lời đúng . –

Trả lời

2

Đề xuất cá nhân của tôi không phải là để thực hiện các phương pháp này chút nào, bởi vì làm như vậy sẽ tải trong nhiều trường hợp không thực sự cần thiết.

Ngoài ra, nếu bạn không di chuyển thực thể qua các phiên, bạn sẽ không bao giờ cần điều này. Và ngay cả khi bạn làm như vậy, bạn luôn có thể so sánh bằng Id khi cần.

+0

Điều gì sẽ xảy ra khi bạn có một bộ sưu tập proxy và một người không có proxy? So sánh với bộ sưu tập đó ** sẽ thất bại **. – TheCloudlessSky

+0

@thecloudlesssky chỉ cần so sánh bằng id theo cách thủ công. Bạn có thể sử dụng LINQ cho các đối tượng cho một phép chiếu nếu bạn cần nó. –

+0

Chắc chắn, nhưng bạn không thể kiểm soát cách 'collection.Remove (entity)' so sánh. Kiểm tra các mẫu trong bài viết của tôi dưới đây để xem làm thế nào điều này có thể thất bại. – TheCloudlessSky

5

Có!

Bạn nên ghi đè EqualsGetHashCode. Tuy nhiên, bạn không nên đang thực hiện giá trị bình đẳng (Name == other.Name && Age == other.Age), bạn nên làm bình đẳng danh tính!

Nếu không, bạn rất có thể sẽ gặp phải việc so sánh proxy của pháp nhân với thực thể thực và sẽ là khốn khổ để gỡ lỗi. Ví dụ:

public class Blog : EntityBase<Blog> 
{ 
    public virtual string Name { get; set; } 

    // This would be configured to lazy-load. 
    public virtual IList<Post> Posts { get; protected set; } 

    public Blog() 
    { 
     Posts = new List<Post>(); 
    } 

    public virtual Post AddPost(string title, string body) 
    { 
     var post = new Post() { Title = title, Body = body, Blog = this }; 
     Posts.Add(post); 
     return post; 
    } 
} 

public class Post : EntityBase<Post> 
{ 
    public virtual string Title { get; set; } 
    public virtual string Body { get; set; } 
    public virtual Blog Blog { get; set; } 

    public virtual bool Remove() 
    { 
     return Blog.Posts.Remove(this); 
    } 
} 

void Main(string[] args) 
{ 
    var post = session.Load<Post>(postId); 

    // If we didn't override Equals, the comparisons for 
    // "Blog.Posts.Remove(this)" would all fail because of reference equality. 
    // We'd end up be comparing "this" typeof(Post) with a collection of 
    // typeof(PostProxy)! 
    post.Remove(); 

    // If we *didn't* override Equals and *just* did 
    // "post.Blog.Posts.Remove(post)", it'd work because we'd be comparing 
    // typeof(PostProxy) with a collection of typeof(PostProxy) (reference 
    // equality would pass!). 
} 

Dưới đây là một lớp cơ sở ví dụ nếu bạn đang sử dụng int như bạn Id (mà cũng có thể được tóm tắt để any identity type):

public abstract class EntityBase<T> 
    where T : EntityBase<T> 
{ 
    public virtual int Id { get; protected set; } 

    protected bool IsTransient { get { return Id == 0; } } 

    public override bool Equals(object obj) 
    { 
     return EntityEquals(obj as EntityBase<T>); 
    } 

    protected bool EntityEquals(EntityBase<T> other) 
    { 
     if (other == null) 
     { 
      return false; 
     } 
     // One entity is transient and the other is not. 
     else if (IsTransient^other.IsTransient) 
     { 
      return false; 
     } 
     // Both entities are not saved. 
     else if (IsTransient && other.IsTransient) 
     { 
      return ReferenceEquals(this, other); 
     } 
     else 
     { 
      // Compare transient instances. 
      return Id == other.Id; 
     } 
    } 

    // The hash code is cached because a requirement of a hash code is that 
    // it does not change once calculated. For example, if this entity was 
    // added to a hashed collection when transient and then saved, we need 
    // the same hash code or else it could get lost because it would no 
    // longer live in the same bin. 
    private int? cachedHashCode; 

    public override int GetHashCode() 
    { 
     if (cachedHashCode.HasValue) return cachedHashCode.Value; 

     cachedHashCode = IsTransient ? base.GetHashCode() : Id.GetHashCode(); 
     return cachedHashCode.Value; 
    } 

    // Maintain equality operator semantics for entities. 
    public static bool operator ==(EntityBase<T> x, EntityBase<T> y) 
    { 
     // By default, == and Equals compares references. In order to 
     // maintain these semantics with entities, we need to compare by 
     // identity value. The Equals(x, y) override is used to guard 
     // against null values; it then calls EntityEquals(). 
     return Object.Equals(x, y); 
    } 

    // Maintain inequality operator semantics for entities. 
    public static bool operator !=(EntityBase<T> x, EntityBase<T> y) 
    { 
     return !(x == y); 
    } 
} 
+1

Nếu một thực thể tạm thời có 'GetHashCode' được lấy và không chuyển đổi, nó có thể so sánh với một thực thể khác không có mã băm của nó trong khi nó tạm thời, nhưng mã băm của nó sẽ không bằng với thực thể kia.Bất cứ khi nào một đối tượng trở thành tương đương với một thứ gì đó không phải trước đó, mã băm của nó phải bằng mã băm của đối tượng khác, và bất cứ khi nào một đối tượng đi vào sự tồn tại. đối tượng khác. Điều cần thiết là có một 'ConcurrentDictionary ' và ... – supercat

+1

... có mỗi 'EntityBase ' giữ một tham chiếu đến một 'Object' được liên kết với id đó; nếu 'GetHashCode' được gọi trước khi một thực thể được duy trì, nó có thể tạo ra một' Object' mà sau đó sẽ được liên kết với ID mà nó nhận được khi đối tượng được duy trì. Sau đó, nó có thể sử dụng giá trị 'GetHashCode' của đối tượng đó là của riêng nó và các thực thể trong tương lai có ID mà người nhận tạm thời cuối cùng nhận được có thể nhận được mã thông báo nhận dạng giống nhau. Định kỳ bảng sẽ phải được chà của các mục mà 'WeakReference' đã chết. – supercat

+0

@supercat Bạn có muốn tạo một ví dụ về những gì bạn đang đề xuất không? Việc thực hiện tôi có ở trên là khuyến cáo thông thường từ các bài viết NH. Bạn cũng có thể cung cấp một mẫu nhanh về cách nó có thể thất bại không? Cảm ơn! – TheCloudlessSky