2

MVCアプリケーションのEntityFramework4.3でかなり奇妙な問題が発生しています。DbContextのUnitofWorkラッパーを使用しており、MVCアプリケーションでは、Unityを使用してこのUOWをリポジトリに渡し、リポジトリをコントローラーに渡します。UOWタイプをに登録しましたHierarchicalLifetimeManager

エラーを発生させるデータベースにエンティティを永続化しようとすると、たとえばデータベースがUNIQUE制約違反をスローした場合、エンティティはEF内に保持されObjectStateManagerます。したがって、アプリケーションに戻ってエラーを修正し、新しいエンティティを(エラーなしで)保存すると、EFは最初に古い無効なオブジェクトを再度追加しようとするため、同じエラーで失敗します。

ここで何が欠けていますか?無効なオブジェクトはEFによって完全に忘れられるべきであり、これは自動的に行われると私は信じています。しかし、明らかにそうではありません。

オブジェクトを永続化するためにDbContextにオブジェクトを追加するには、次のコマンドが呼び出されます(ここbaseで、はDbContextです)。

base.Set<TEntity>().Add(objectToPersist);

そして、データベースへの変更をコミットするために、私は次のように呼び出します。

base.SaveChanges();

これはエラーをスローします。

4

3 に答える 3

3

無効なオブジェクトはEFによって完全に忘れられるべきであり、これは自動的に行われると私は信じています。しかし、明らかにそうではありません。

そうではなく、例外が発生したときにエンティティがコンテキストから自動的に切り離されるとは聞いたことがありません。

この問題に対処するには、基本的に2つのオプションがあります。一意キー制約違反の例を含む単純なモデルを示します。

public class Customer
{
    // so we need to supply unique keys manually
    [DatabaseGenerated(DatabaseGeneratedOption.None)]
    public int Id { get; set; }
    public string Name { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Customer> Customers { get; set; }
}

class Program
{
    static void Main(string[] args)
    {
        Database.SetInitializer(new DropCreateDatabaseAlways<MyContext>());

        using (var ctx = new MyContext())
        {
            var customer = new Customer { Id = 1, Name = "X" };
            ctx.Customers.Add(customer);
            ctx.SaveChanges();
        }
        // Now customer 1 is in database

        using (var ctx = new MyContext())
        {
            var customer = new Customer { Id = 1, Name = "Y" };
            ctx.Customers.Add(customer);

            try
            {
                ctx.SaveChanges();
                // will throw an exception because customer 1 is already in DB
            }
            catch (DbUpdateException e)
            {
                // customer is still attached to context and we only
                // need to correct the key of this object
                customer.Id = 2;
                ctx.SaveChanges();
                // no exception
            }
        }
    }
}

上記が推奨される解決策です。コンテキストにアタッチされているオブジェクトを修正します。

何らかの理由で新しいオブジェクトを作成する必要がある場合は、古いオブジェクトをコンテキストから切り離す必要があります。そのオブジェクトはまだ状態Addedにあり、呼び出したときにEFはオブジェクトを再度保存しようとしSaveChanges、以前と同じ例外が発生します。

古いオブジェクトを切り離すと、次のようになります。

            try
            {
                ctx.SaveChanges();
                // will throw an exception because customer 1 is already in DB
            }
            catch (DbUpdateException e)
            {
                ctx.Entry(customer).State = EntityState.Detached;
                // customer is now detached from context and
                // won't be saved anymore with the next SaveChanges

                // create new object adn attach this to the context
                var customer2 = new Customer { Id = 2, Name = "Y" };
                ctx.Customers.Add(customer2);
                ctx.SaveChanges();
                // no exception
            }

関係が関係している場合、この手順は難しい場合があります。たとえばcustomer、注文のリストと関係があるcustomer場合、注文がコンテキストにも添付されていると、オブジェクトをデタッチすると、顧客とその注文の間の参照が削除されます。新しいとの関係を再確立する必要がありcustomer2ます。

したがって、アタッチされたオブジェクトを変更して、正しい状態にすることをお勧めします。または、このような制約違反は通常、コードのバグを示しているため、またはマルチユーザー環境では適切な楽観的同時実行性チェックで処理する必要があるため、アプリケーションをクラッシュさせます。

于 2012-05-16T13:46:57.007 に答える
1

無効なオブジェクトについて気が変わったことをEFに伝える必要があるようです。

base.Set()。Remove(objectToPersist);

于 2012-05-02T16:39:40.897 に答える
1

変更をリセットする場合は、ObjectContextをnullに設定して、再インスタンス化できます。

于 2012-05-22T22:29:38.333 に答える