2012-03-22 8 views
1

アクションを介してフォームからMVCコントローラに戻されるオブジェクトがある状況があります。 FormCollectionは使用しませんが、直接クラスを使用します。エンティティフレームワーク4.3コントローラのアクションのパラメータからオブジェクトをアタッチする

[HttpPost] 
    public ActionResult AjaxUpdate(Customer customer) { ... 

Customerオブジェクトには、更新されたように見えるが、コンテキスト上でSaveDatabase()を使用すると単に機能しないオブジェクトがあります。

それは私がアクションで使用していた作品にするには:

myDbContext.Customers.Attach(customer) 
//...Code here that set to the customer.SubObject a real object from the database so I am sure that the SubObject contain an id which is valid and the datacontext is aware of it... 
myDbContext.Entry(customer).State = EntityState.Modified; 

はそれでも、私は、「ストアUPDATE、INSERT、またはステートメントが行(0)の予想外の数に影響を与え削除」に関する例外を持っていました私が使用して削除することができたこと:

Database.ObjectContext().Refresh(RefreshMode.ClientWins,customer); 

だから、私の質問、 をワープし、なぜ私は+アタッチ状態+コールの更新を変更する必要がありません。他のテーブルで参照されているオブジェクトを含むオブジェクトを更新するより良い方法はありません。私はコードの最初のEntity Framework(Pocoオブジェクト)を使用しています。また、Databasecontextから隠されているため、Refreshを使用したくありません。

+0

はまた、あなたを行います

したがって、本来のサブオブジェクトをロードする代替ソリューションはCustomerに外部キープロパティを導入することですコメントの後ろに隠れているコードを削除するとエラーが発生しますか?そうでない場合は、この隠しコードをより詳細にスケッチすると役立つかもしれません。 'Refresh'を使ってオブジェクトグラフの更新を取得する必要性は、「正常ではありません」です。 – Slauma

+0

コメントのコード(この記事では非表示)は、このサブオブジェクトのインスタンスを顧客に取得するためのサブオブジェクトへのクエリです。これは、クライアントがコンボボックス内で新しい値を選択し、データベースからオブジェクトを戻して顧客に割り当てることができるためです(顧客のFKを更新することを目標としています)。 –

+0

サブオブジェクトを取得するために同じコンテキスト 'myDbContext'を使用していますか? 'Customer'クラスのサブオブジェクトの外部キープロパティを持っていますか? – Slauma

答えて

2

EF 4.3.1でコンソールテストプロジェクトを作成しました。コードは私の推測であり、あなたのコメント行とコメントの下にあなたのコメント(私の推測はおそらくプログラムがあなたのエラーを再現しないので、間違っていると思われます):

あなたはprogram.csにコードをコピーし、 EF 4.3.1への参照を追加してください:

using System.Data; 
using System.Data.Entity; 

namespace EFUpdateTest 
{ 
    public class Customer 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 

     public int SubObjectId { get; set; } 
     public SubObject SubObject { get; set; } 
    } 

    public class SubObject 
    { 
     public int Id { get; set; } 
     public string Something { get; set; } 
    } 

    public class CustomerContext : DbContext 
    { 
     public DbSet<Customer> Customers { get; set; } 
     public DbSet<SubObject> SubObjects { get; set; } 
    } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      int customerId = 0; 
      int subObject1Id = 0; 
      int subObject2Id = 0; 
      using (var ctx = new CustomerContext()) 
      { 
       // Create customer with subobject 
       var customer = new Customer { Name = "John" }; 
       var subObject = new SubObject { Something = "SubObject 1" }; 
       customer.SubObject = subObject; 
       ctx.Customers.Add(customer); 

       // Create a second subobject, not related to any customer 
       var subObject2 = new SubObject { Something = "SubObject 2" }; 
       ctx.SubObjects.Add(subObject2); 

       ctx.SaveChanges(); 

       customerId = customer.Id; 
       subObject1Id = subObject.Id; 
       subObject2Id = subObject2.Id; 
      } 

      // New context, simulate detached scenario -> MVC action 
      using (var ctx = new CustomerContext()) 
      { 
       // Changed customer name 
       var customer = new Customer { Id = customerId, Name = "Jim" }; 
       ctx.Customers.Attach(customer); 

       // Changed reference to another subobject 
       var subObject2 = ctx.SubObjects.Find(subObject2Id); 
       customer.SubObject = subObject2; 

       ctx.Entry(customer).State = EntityState.Modified; 

       ctx.SaveChanges(); 
       // No exception here. 
      } 
     } 
    } 
} 

これは例外なく動作します。問題は、エラーの原因となる可能性のあるコードとは何が違うのですか?

編集

あなたは、顧客のクラスに外部キープロパティSubObjectIdを持っていないあなたのコメントに:私は、エラーを再現できる上に、私はプログラム例ではプロパティを削除した場合。

ソリューションを使用すると、関係変更する前に、データベースから元のサブオブジェクトをロードすることです:

// Changed customer name 
var customer = new Customer { Id = customerId, Name = "Jim" }; 
ctx.Customers.Attach(customer); 

// Load original SubObject from database 
ctx.Entry(customer).Reference(c => c.SubObject).Load(); 

// Changed reference to another subobject 
var subObject2 = ctx.SubObjects.Find(subObject2Id); 
customer.SubObject = subObject2; 

ctx.Entry(customer).State = EntityState.Modified; 

ctx.SaveChanges(); 
// No exception here. 

外部キープロパティがなければ、あなたが独立協会持つオブジェクトは、すべての参照を含むことが必要です変更する前にデータベースの状態を表す必要があります。 SubObjectの参照をcustomerに設定しない場合、EFはデータベースの元の状態がそのサブジェクトを参照していないことを前提としています。 UPDATE文の生成されたSQLは、次のようなWHERE句が含まれます。顧客はDB [SubObject_Id]でサブオブジェクトを持っている場合

WHERE [Customers].[Id] = 1 AND [Customers].[SubObject_Id] IS NULL 

ないでNULL、条件が満たされないとUPDATEが発生しません(または「予期しない行数0」で起こる)。

あなたが外部キープロパティを持っている場合、問題は発生しません(外部キー協会は):この場合の句は唯一であるザ・WHERE:だから

WHERE [Customers].[Id] = 1 

、元だかは重要ではありませんSubObjectおよびSubObjectIdの値。それでも、値nullのままにすることはできますが、UPDATEは機能します。関係が必要とされていない場合には、

public int SubObjectId { get; set; } 

または::

public int? SubObjectId { get; set; } 
+0

Thxです。私はあなたに+1を与える。私を比較させてください。最初の一見で、私はあなたがしていることを正確にやっています。唯一の違いは、サブオブジェクトは、PKではなくコード(これは異なる方法でもユニークです)からEFから取得されることです。しかし、あなたが私に与えてくれたことからもっと調べてみましょう。私はあなたに戻ってきます。 –

+0

申し訳ありませんが、ここに大きな違いがあります。 SubObjectIDにはCustomer内のIDを使用しますが、私は使用しません。内部例外をチェックしても例外をチェックすると、「関係の外部キープロパティを公開していないエンティティを保存する際にエラーが発生しました。EntityEntriesプロパティは、単一のエンティティを例外のソースとして識別できないためnullを返しますエンティティ型に外部キーのプロパティを公開することで、保存時の例外の処理を簡単に行うことができます。詳細については、InnerExceptionを参照してください。 –

+0

@PatrickDesjardins:OK、あなたの質問のもとであなたの最後のコメントで言ったので、あなたはFKプロパティを持っていたと仮定していました。私は今それを削除し、再度テストしました。このテストの結果は、私の答えの編集セクションにあります。 – Slauma

関連する問題