6

EF の DbSet.Add() がかなり遅いことに気付きました。少しグーグルすると、最大180倍のパフォーマンス向上を約束するSOの答えが見つかりました。

https://stackoverflow.com/a/7052504/141172

IEquatable<T>ただし、回答で提案されているように実装する方法が正確にはわかりません。

MSDN によると、 を実装する場合はIEquatable<T>、 と もオーバーライドする必要がありEquals()ますGetHashCode()

多くの POCO と同様に、私のオブジェクトはmutableです。データベースにコミットされる前 ( SaveChanges())、新しいオブジェクトの ID は 0 です。オブジェクトが保存された、ID は IEquatable、Equals()、および GetHashCode() を実装するための理想的な基盤として機能します。

変更可能なプロパティをハッシュ コードに含めることは賢明ではありません。MSDN によると、

2 つのオブジェクトを比較して等しい場合、各オブジェクトの GetHashCode メソッドは同じ値を返す必要があります。

IEquatable<T>プロパティごとの比較 (例: ) として実装し、 this.FirstName == other.FirstNameEquals() と GetHashCode() をオーバーライドする必要はありませんか?

POCO が EntityFramework コンテキストで使用されている場合、Id フィールドに特別な注意を払う必要がありますか?

4

3 に答える 3

3

同じ質問の解決策を探していたところ、あなたの質問に出会いました。これが私が試している解決策です。それがあなたのニーズを満たしているかどうかを確認してください:

まず、すべての 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 の実装を使用しました。これにより、上記のリンクされた回答の平等に関する制約が満たされます。

于 2012-11-17T05:51:09.307 に答える
1

多くの POCO と同様に、私のオブジェクトは変更可能です

ただし、主キーであるフィールドで可変であってはなりません。定義によると、そうでなければ、とにかく後で賢明な痛みのデータベースの世界にいます。

主キーのフィールドでのみ HashCode を生成します。

Equals() は、参加しているオブジェクトが同じハッシュ コードを持つ場合に true を返さなければなりません

BZZZ - エラー。

ハッシュコードは2倍です。2 つのオブジェクトが異なる値と smae ハッシュコードを持つ可能性があります。hsahsode は int (32 ビット) です。文字列の長さは 2GB です。可能なすべての文字列を個別のハッシュコードにマップすることはできません。

2 つのオブジェクトのハッシュコードが同じ場合、それらは異なる可能性があります。2 つのオブジェクトが同じ場合、異なるハッシュコードを持つことはできません。

同じハッシュコードを持つオブジェクトに対して Equals が true を返さなければならないという考えはどこから得たのでしょうか?

また、PCO であろうとなかろうと、データベースにマップされ、リレーションで使用されるオブジェクトには、安定した主キー (ハッシュコード計算の実行に使用できる) が必要です。この STIL を持たないオブジェクトには、(SQL Server の要件に従って) 主キーが必要です。ここでは、シーケンス/人工的な主キーが機能します。繰り返しますが、これを使用して HashCode 計算を実行します。

于 2012-03-20T06:57:30.930 に答える
0

まず最初に:申し訳ありませんが私の不自由な英語:)

TomTom が言うように、PK/Id をまだ受け取っていないという理由だけで変更可能であってはなりません...

私たちの EF:CF システムでは、新しい POCO ごとに生成された負の ID (基本クラス ctor で割り当てられるか、ProxyTracking を使用する場合は ObjectMaterialized イベントで割り当てられる) を使用します。その非常に単純なアイデア:

public static class IdKeeper
{
  private static int m_Current = int.MinValue;
  private static Next()
  {
    return ++m_Current;
  }
}

EF は変更を db にコミットする前に PK で POCO をソートし、「-1、-2、-3」を使用すると POCO が反転して保存されるため、MinValue と Incremen は重要です。 ) 理想的ではないかもしれません。

public abstract class IdBase
{
  public virtual int Id { get; set; }
  protected IdBase()
  {
    Id = IdKeeper.Next();
  }
}

POCO が DB からマテリアライズされる場合、彼の Id は実際の PK と SaveChanges() を呼び出したときにオーバーライドされます。おまけとして、「まだ保存されていない」POCO ID はすべて一意になります (いつか便利になるはずです ;))

2 つの POCO を IEquatable (なぜ dbset の動作が遅いのか) と比較するのは簡単です。

public class Person
  : IdBase, IEquatable<Person>
{
  public virtual string FirstName { get; set; }

  public bool Equals(Person other)
  {
    return Id == other.Id;
  }
}
于 2012-04-27T14:14:59.523 に答える