2013-07-05 27 views
6

エンティティフレームワーク5を使用するMVCアプリケーションがあります。エンティティを作成または更新するコードがいくつかありますが、更新されたデータに対して何らかの操作を実行する必要があります。これらの操作の中には、ナビゲーションプロパティへのアクセスが必要なものがあり、更新することができません。ここEntity Framework 5 - 変更を保存した直後にDbContextをリフレッシュする

は、例えば(私は簡略化コード)

モデル

class User : Model 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
} 

class Car : Model 
{ 
    public Guid Id { get; set; } 
    public Guid DriverId { get; set; } 
    public virtual User Driver { get; set; } 

    [NotMapped] 
    public string DriverName 
    { 
     get { return this.Driver.Name; } 
    } 
} 

コントローラ

public CarController 
{ 
    public Create() 
    { 
     return this.View(); 
    } 

    [HttpPost] 
    public Create(Car car) 
    { 
     if (this.ModelState.IsValid) 
     { 
      this.Context.Cars.Create(booking); 
      this.Context.SaveChanges(); 

      // here I need to access some of the resolved nav properties 
      var test = booking.DriverName; 
     } 

     // error handling (I'm removing it in the example as it's not important) 
    } 
} 

は、上記の例では、作成方法のためのものであるが、私も持っています非常に似ているUpdateメソッドの同じ問題オブジェクトをGETアクションでコンテキストから取り出し、Updateメソッドを使用してPOSTアクションに格納します。

public virtual void Create(TObject obj) 
{ 
    return this.DbSet.Add(obj); 
} 

public virtual void Update(TObject obj) 
{ 
    var currentEntry = this.DbSet.Find(obj.Id); 
    this.Context.Entry(currentEntry).CurrentValues.SetValues(obj); 
    currentEntry.LastModifiedDate = DateTime.Now; 
} 

は今、私はグーグルやスタックに見つかりましたが、何も私のために働いているように思わないいくつかの異なるアプローチを試みました。

私の最新の試みでは、SaveChangesメソッドを呼び出してデータベースからデータを再クエリーした後、リロードを強制しようとしました。ここに私がしたことがあります。

私はすぐ後にすぐに私のHTTPCreateUpdateSaveChanges呼び出しの後のコード行を追加することで、私は更新されたオブジェクトデータを取得しようとした

public int SaveChanges() 
{ 
    var rowsNumber = this.Context.SaveChanges(); 
    var objectContext = ((IObjectContextAdapter)this.Context).ObjectContext; 
    objectContext.Refresh(RefreshMode.StoreWins, this.Context.Bookings); 

    return rowsNumber; 
} 

保存オブジェクトコンテキストをリフレッシュするSaveChanges方法ovewriteましたアクション:

car = this.Context.Cars.Find(car.Id); 

残念ながらナビゲーションプロパティはまだnullです。データを変更した直後にDbContextを適切に更新するにはどうすればよいですか?私はもともと私が回避策を知っているが、それは醜いだと私はそれを好きではないことを言及するのを忘れ

EDIT

。ナビゲーションプロパティを使用するたびに、それがnullであるかどうかを確認することができ、そうであれば手動で新しいDbContextを作成してデータを更新することができます。しかし、私は本当にこのようなハックを避けたいです。

class Car : Model 
{  
    [NotMapped] 
    public string DriverName 
    { 
     get 
     { 
      if (this.Driver == null) 
      { 
       using (var context = new DbContext()) 
       { 
        this.Driver = this.context.Users.Find(this.DriverId); 
       } 
      } 

      return this.Driver.Name; 
     } 
    } 
} 

答えて

4

問題は、おそらくあなたがコンテキストに追加された項目は、遅延ロードするために必要なすべてのコンポーネントとproxyではないという事実によるものです。 SaveChanges()に電話した後でも、にプロキシされたインスタンスに変換されません。

DbSetを使用することをお勧めします。()メソッドを作成し、ネットワーク経由で受信エンティティからすべての値を越えコピー:SetValues()が問題を与えている場合

public virtual TObject Create(TObject obj) 
{ 
    var newEntry = this.DbSet.Create(); 
    this.Context.Entry(newEntry).CurrentValues.SetValues(obj); 
    return newEntry; 
} 

UPDATE

その後、私はあなたが転送しautomapperをしようと提案します新しいプロキシインスタンスをDbSetに送信する前に、渡されたエンティティから作成されたプロキシにデータを転送します。このような何か:

private bool mapCreated = false; 
public virtual TObject Create(TObject obj) 
{ 
    var newEntry = this.DbSet.Create(); 

    if (!mapCreated) 
    { 
     Mapper.CreateMap(obj.GetType(), newEntry.GetType()); 
     mapCreated = true; 
    } 
    newEntry = Mapper.Map(obj, newEntry); 

    this.DbSet.Add(newEntry; 
    return newEntry; 
} 
+0

を読み込みますか? 'this.Context.Cars.Find(id)'以外の元のオブジェクトも取得する必要がありますか? – RaYell

+0

'Find'はデータベースからプロキシオブジェクトを返しますが、このトランザクション中にローカルに追加されたオブジェクトを見つけて更新すると、追加したオブジェクトが見つかります。 – qujck

+0

私はあなたのソリューションに従ってみましたが、それは 'System.InvalidOperationExceptionにつながります:エンティティがコンテキスト内に存在しないため、タイプ 'Car'のエンティティに対してMember 'CurrentValues'を呼び出すことができません。コンテキストにエンティティを追加するには、DbSet のAddメソッドまたはAttachメソッドを呼び出します。 'this.Context.Set ().Attach(newEntry);を最初の行の直後に追加しました。 System.InvalidOperationException:プロパティ 'Id'はオブジェクトのキー情報の一部であり、変更することはできません。 ' – RaYell

0

私は次の回避策を使用します。エンティティを切り離し、再び何私の `Update`方法について

public T Reload<T>(T entity) where T : class, IEntityId 
{ 
    ((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity); 
    return _dbContext.Set<T>().FirstOrDefault(x => x.Id == entity.Id); 
} 
関連する問題