2012-05-02 7 views
2

私のMVCアプリケーションでEntity Framework 4.3にかなり奇妙な問題があります。私はDbContextの周りにUnit Unworkラッパーを使用しています。私のMVCアプリケーションでは、Unityを使用してこのUOWをリポジトリに渡し、リポジトリをコントローラに渡します。 HierarchicalLifetimeManagerでUOWタイプを登録しました。EFが無効なオブジェクトを保持しようとしています

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

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

は、次のコマンドは(baseがあるDbContext)に呼び出されます:

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

し、データベースに変更をコミットするために、私は呼ん:

base.SaveChanges(); 

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

+0

あなたはUoWにHierarchicalLifetimeManagerを使用していると言います。私はあなたが手動またはUnity.Mvc3経由でリクエストごとに子コンテナを作成していると仮定しています。さもなければ、UoWはあらゆる種類の問題を引き起こすシングルトンになります。 –

+0

エラーを投函してください。 –

答えて

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の状態にあり、SaveChangesを呼び出して以前と同じ例外が発生した場合、EFはオブジェクトを再度保存しようとします。古いオブジェクトを取り外し

は次のようになります。

  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との関係を再確立する必要があります。

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

1

はあなたが無効なオブジェクトについてのあなたの心を変更EF伝える必要がありますようになります。

base.Setを()(objectToPersist)を削除します。

+0

本当に必要ですか?私はEFでこの問題に遭遇したことはありません - このアプリケーションのみ。私は、EFは通常、新しいオブジェクトを永続化しようとしているときにSqlException例外に対処するときにこれをデフォルトで行うと考えています。 –

1

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

関連する問題