Я наткнулся на ваш вопрос в поисках решения того же вопроса. Вот решение, которое я пробую, посмотрите, соответствует ли оно вашим потребностям:
Во-первых, все мои POCO происходят из этого абстрактного класса:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
private readonly Guid _guid = Guid.NewGuid();
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
if (obj.GetType() != typeof (T))
{
return false;
}
return Equals((T)obj);
}
public override int GetHashCode()
{
return _guid.GetHashCode();
}
}
Я создал поле Guid, доступное только для чтения, которое я использую в переопределении GetHashCode (). Это гарантирует, что если бы я поместил производное POCO в словарь или что-то еще, использующее хеш, я бы не осиротел, если бы я вызвал .SaveChanges () в промежуточный период, а поле ID было обновлено базовым классом Это одна часть, я не уверен, что она полностью правильная, или она лучше, чем просто Base.GetHashCode ()? . Я абстрагировал метод Equals (T other), чтобы гарантировать, что реализующие классы должны были реализовать его каким-либо осмысленным способом, скорее всего с полем ID. Я поместил переопределение Equals (object obj) в этот базовый класс, потому что оно, вероятно, будет одинаковым для всех производных классов.
Это будет реализация абстрактного класса:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
}
Свойство ID установлено в качестве первичного ключа в базе данных, и EF это знает. Идентификатор равен 0 для вновь созданных объектов, затем устанавливается уникальное положительное целое число в .SaveChanges (). Таким образом, в переопределенном методе Equals (Species other) нулевые объекты, очевидно, не равны, очевидно, одни и те же ссылки, тогда нам нужно только проверить, равен ли ID == 0. Если это так, мы скажем, что два объекта одного типа что оба имеют идентификаторы 0 не равны. В противном случае мы будем говорить, что они равны, если их свойства одинаковы.
Я думаю, что это охватывает все соответствующие ситуации, но, пожалуйста, сообщите, если я ошибаюсь. Надеюсь, это поможет.
=== Редактировать 1
Я думал, что мой GetHashCode () был неправильным, и я посмотрел на этот https://stackoverflow.com/a/371348/213169 ответ относительно предмета. Приведенная выше реализация нарушила бы ограничение на то, что объекты, возвращающие Equals () == true, должны иметь одинаковый хеш-код.
Вот мой второй удар:
public abstract class BasePOCO <T> : IEquatable<T> where T : class
{
#region IEquatable<T> Members
public abstract bool Equals(T other);
#endregion
public abstract override bool Equals(object obj);
public abstract override int GetHashCode();
}
И реализация:
public class Species : BasePOCO<Species>
{
public int ID { get; set; }
public string LegacyCode { get; set; }
public string Name { get; set; }
public override bool Equals(Species other)
{
if (ReferenceEquals(null, other))
{
return false;
}
if (ReferenceEquals(this, other))
{
return true;
}
return ID != 0 &&
ID == other.ID &&
LegacyCode == other.LegacyCode &&
Name == other.Name;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj))
{
return false;
}
if (ReferenceEquals(this, obj))
{
return true;
}
return Equals(obj as Species);
}
public override int GetHashCode()
{
unchecked
{
return ((LegacyCode != null ? LegacyCode.GetHashCode() : 0) * 397) ^
(Name != null ? Name.GetHashCode() : 0);
}
}
public static bool operator ==(Species left, Species right)
{
return Equals(left, right);
}
public static bool operator !=(Species left, Species right)
{
return !Equals(left, right);
}
}
Итак, я избавился от Guid в базовом классе и переместил GetHashCode в реализацию. Я использовал реализацию GetHashCode от Resharper со всеми свойствами, кроме ID, так как ID может измениться (не нужно оставлять сирот). Это встретит ограничение на равенство в связанном ответе выше.