Search code examples
c#entity-frameworkentity-framework-6

When Overriding GetHashCode(), Entity Framework 6 Throws Exception on SaveChanges()


Executing the following two lines

DBContext.DbSet.Remove(DBContext.DbSet.First(x => x.Id == someExistingId));
DBContext.SaveChanges();

results in the following exception being thrown on SaveChanges() when I override GetHashCode() on the DbSet entity type.

Adding a relationship with an entity which is in the Deleted state is not allowed.

I'm using a T4 Template to generate POCO objects and overriding GetHashCode() in a partial. If I comment out only the GetHashCode() override, the code executes as expected.

The GetHashCode() override just returns Id.GetHashCode(). Id is an int.

Is there some restriction related to overriding GetHashCode() with Entity Framework POCOs?


Solution

  • From the entity framework documentation:

    • You should still follow the guidelines (Eric Lippert has a great post on this) for implementing Equals and GetHashCode
    • If your hashcode/equality algorithm cannot guarantee immutability and uniqueness at all times then you need to make sure that your collection navigation properties use reference equality for comparisons. For HashSet pass System.Data.Entity.Infrastructure.ObjectReferenceEqualityComparer to the constructor (or create your own reference-based equality comparer, something like the snippet below should work). Do not use List as it will always use the overridden Equals method for methods like Remove.
    public class Category
    {
        public Category()
        {
            Products = new HashSet<Product>(new ObjectReferenceEqualityComparer());
        }
    
        public string CategoryId { get; set; }
        public string Name { get; set; }
    
        public virtual ICollection<Product> Products { get; set; }
    }
    [Serializable]
    public sealed class ObjectReferenceEqualityComparer : IEqualityComparer<object>
    {
        bool IEqualityComparer<object>.Equals(object x, object y)
        {
            return ReferenceEquals(x, y);
        }
    
        int IEqualityComparer<object>.GetHashCode(object obj)
        {
            return RuntimeHelpers.GetHashCode(obj);
        }
    }