同じ質問の解決策を探していたところ、あなたの質問に出会いました。これが私が試している解決策です。それがあなたのニーズを満たしているかどうかを確認してください:
まず、すべての 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();
}
}
GetHashCode() オーバーライドで使用している読み取り専用の Guid フィールドを作成しました。これにより、派生した 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 はそれを認識しています。新しく作成されたオブジェクトの ID は 0 で、.SaveChanges() で一意の正の整数に設定されます。したがって、オーバーライドされた Equals(Species other) メソッドでは、null オブジェクトは明らかに等しくなく、同じ参照は明らかに等しく、ID == 0 かどうかを確認するだけで済みます。そうであれば、同じ型の 2 つのオブジェクトであると言えます。両方の ID が 0 であることは等しくありません。それ以外の場合、それらのプロパティがすべて同じであれば、それらは等しいと言えます。
これで関連するすべての状況がカバーされると思いますが、間違っている場合はご指摘ください。お役に立てれば。
=== 編集 1
GetHashCode() が正しくないと思っていたので、この件名に関するhttps://stackoverflow.com/a/371348/213169の回答を見ました。上記の実装は、Equals() == true を返すオブジェクトは同じハッシュコードを持たなければならないという制約に違反します。
これが私の2番目の刺し傷です。
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 を実装に移動しました。ID は変更される可能性がある (孤立したくない) ため、ID を除くすべてのプロパティで Resharper の GetHashCode の実装を使用しました。これにより、上記のリンクされた回答の平等に関する制約が満たされます。