2009-05-22 10 views
6

Bir EntityObject EntityKey ayarlamam gerekir. Onun türünü ve kimlik değerini biliyorum. Veritabanını gereksiz yere sorgulamak istemiyorum.EntityKey ve ApplyPropertyChanges()

Bu çalışır ...

// 
// POST: /Department/Edit/5 

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department Model) 
{ 
    Model.EntityKey = (from Department d in db.Department 
         where d.Id == id 
         select d).FirstOrDefault().EntityKey; 
    db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model); 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 

Bu başarısız ...

// 
// POST: /Department/Edit/5 

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department Model) 
{ 
    String EntitySetName = db.DefaultContainerName + "." + Model.GetType().Name; 
    Model.EntityKey = new System.Data.EntityKey(EntitySetName, "Id", Model.Id); 
    db.ApplyPropertyChanges(Model.EntityKey.EntitySetName, Model); 
    db.SaveChanges(); 
    return RedirectToAction("Index"); 
} 

ApplyPropertyChanges() çizgisi bu durumla başarısız:

ObjectStateManager değil , birile bir ObjectStateEntry içerir 'Sample.Models.Department' türündeki bir nesneye başvuru.

İki EntityKey eşittir. İkinci kod bloğu neden başarısız? Bunu nasıl düzeltebilirim?

cevap

9

İkinci kod bloğunuzun başarısız olmasının nedeni, EF nesneyi ObjectStateManager öğesinde bulamamasıdır - örneğin db'den nesneleri çektiğinde bunları izleyerek onları izleyebilmeleri için devlet yöneticisine gönderir. Identity Map modeli. EntityKey'e sahip olmanıza rağmen, nesneniz devlet yöneticisinde değildir, dolayısıyla EF değişiklikleri devam ettiremez. Nesneyi devlet yöneticisine yerleştirerek bunu başarabilirsin ama bu konuda biraz sinsi davranıyorsun.

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department model) 
{ 
    var entitySetName = db.DefaultContainerName + "." + model.GetType().Name; 
    var entityKey = new System.Data.EntityKey(entitySetName, "Id", model.Id); 

    db.Attach(new Department{Id = id, EntityKey = entityKey}); 
    db.AcceptAllChanges(); 

    db.ApplyPropertyChanges(entitySetName, model); 
    db.SaveChanges(); 
} 

... ama çok temiz değil:

Bu çalışır. Temel olarak, bu bir "boş" nesneyi yalnızca bir varlık anahtarıyla birleştirir, tüm değişiklikleri kabul eder ve ardından ApplyPropertyChanges'i gerçek gerçek güncellenmiş değerlerle çağırır.

Burada bir uzantı yöntemiyle aynı şey var - bu, birincil anahtar için tek bir db sütunu kullanan her şey için çalışmalıdır.

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department model) 
{ 
    db.ApplyDetachedPropertyChanges(model, x => x.Id); 
    db.SaveChanges(); 
} 

ve uzatma yöntemi:

public static class EfExtensions 
{ 
    public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity, Func<T, int> getIdDelegate) 
    where T : EntityObject 
    { 
    var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name; 
    var id = getIdDelegate(entity); 
    var entityKey = new EntityKey(entitySetName, "Id", id); 

    db.Attach(new Department {Id = id, EntityKey = entityKey}); 
    db.AcceptAllChanges(); 

    db.ApplyPropertyChanges(entitySetName, entity); 
    } 
} 
yöntemini çağırarak tek ilginç yanı, nasıl uzantısı yöntemine ikinci argüman olarak bir temsilci aracılığıyla anahtar özelliği bulmak için söylemek gerektiğidir

Uzantı yöntemi AcceptAllChanges'i çağırırken, birden çok öğe üzerinde aynı anda güncelleme yapıyorsanız, buna dikkat etmeniz gerekir; dikkatli değilseniz, kolayca 'kaybedebilirsiniz'. Bu nedenle, bu yaklaşım sadece basit güncelleme senaryoları için uygundur - örn. Bir çok MVC eylem metodu :)

+0

+1. Bu güzeldi! =) Tablonun çoklu kolon PK kullanıyorsa yöntem nasıl değişmeli? –

+0

Sen anahtar belirtmek için KeyValuePair bir IEnumerable aldı uzatma yönteminin bir aşırı yapmak gerekir ve sonra EntityKey karşılık gelen yapıcı aşırı derim. Stype güvenliğini korumak ve KeyValuePair'inizdeki dizelerden geçmekten kaçınmak için, tuşlara yerleştirilecek değerlerle birlikte bir 'anahtar bulma' delegesi dizisi geçirebilirsiniz. –

+1

bu büyük cevap dayanarak, birazcık iyileşti ve ben de bir cevap olarak yayınladığı, bunun dışında kendi küçük yöntemini yaptı. Her türlü nesne nesnesinde çalışır ve delegeleri kullanmaz. –

4
public static class EfExtensions 
{ 
    public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity, Func<T, int> getIdDelegate) 
    where T : EntityObject 
    { 
     var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name; 

     T newEntity = Activator.CreateInstance<T>(); 
     newEntity.EntityKey = db.CreateEntityKey(entitySetName, entity); 

     Type t = typeof(T); 
     foreach(EntityKeyMember keyMember in newEntity.EntityKey.EntityKeyValues) { 
      PropertyInfo p = t.GetProperty(keyMember.Key); 
      p.SetValue(newEntity, keyMember.Value, null); 
     } 

     db.Attach(newEntity); 
     //db.AcceptAllChanges(); 

     db.ApplyPropertyChanges(entitySetName, entity); 
    } 
} 
+0

sayesinde böyle bir şey arıyordum ve gereğini verilen! +1 – camainc

+1

Varlık kümelerim kod üreteci tarafından çoğaltıldığından, "entity.GetType(). Name" üzerine bir "ToPlural()" dizgi uzantısı yöntemi eklemem gerekiyordu. Bunun dışında, kodunuz mükemmel çalıştı. – camainc

2

Aşağıdaki kodları kullanmayı deneyin ve sizin için çalışıyor mu diye bana bildirin.

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department Model) 
{ 
    using (var context = new EntityContext()) 
    { 
     try 
     { 
      Object entity = null; 
      IEnumerable<KeyValuePair<string, object>> entityKeyValues = 
       new KeyValuePair<string, object>[] { 
        new KeyValuePair<string, object>("DepartmentID", id) }; 

      // Create the key for a specific SalesOrderHeader object. 
      EntityKey key = new EntityKey("EntityContext.Deparment", 
                    entityKeyValues); 

      // Get the object from the context or the persisted store by its key. 
      if (context.TryGetObjectByKey(key, out entity)) 
      { 
       context.ApplyPropertyChanges(key.EntitySetName, Model); 
       context.SaveChanges(); 
      } 
      else 
      { 
       // log message if we need 
       //"An object with this key could not be found." 
      }     
     } 
     catch (EntitySqlException ex) 
     { 
      // log message 
     } 
    } 
}  
1

Aynı sorun vardı, diğer sayfalarda çalışıyordu. Bu yorum işe yarayacak mı bilmiyorum ... Ama sonunda keşfettim ki, objetimde (sizin Departmanınızın eşdeğeri), "Kimlik" formunda görünmüyordu ...

Sadece gerçeği ekle (onu kaldırmışken ...) formumda sorunumu çözdü. (onu görmemek için, stil = "görünürlük: gizli" ile ...) Steve Willcock harika uygulanması üzerine iyileştirilmesi

2

, işte öneri.

Size bazı kodu kaydetmek için, orijinal örnek şekilde daha Reflection (.NET bir parçası) kullanır. Ayrıca, sadece bir "Departman" da değil, her tür varlık sınıfını otomatik olarak destekler. Ayrıca, ApplyPropertyChanges yöntemini geçersiz kılar ve yeni ApplyCurrentValues yöntemini kullanır.

yöntem temelde sadece dinamik "İd" özelliğinin değeri almak için yansıma kullanan

yöntemi ve çok ayarlama. Bu tüm zorlukları bir delege ile kaydeder. Bunu kullanarak

public static void ApplyDetachedPropertyChanges<T>(this ObjectContext db, T entity) where T : EntityObject 
{ 
    PropertyInfo idProperty = typeof(T).GetProperty("Id"); 

    var entitySetName = db.DefaultContainerName + "." + entity.GetType().Name; 
    var id = idProperty.GetValue(entity, null); 
    var entityKey = new EntityKey(entitySetName, "Id", id); 

    Type type = entity.GetType(); 
    EntityObject obj = (EntityObject)Activator.CreateInstance(type); 

    idProperty.SetValue(obj, id, null); 
    obj.EntityKey = entityKey; 

    db.Attach(obj); 
    db.AcceptAllChanges(); 

    db.ApplyCurrentValues(entitySetName, entity); 
} 

Kullanımı

de oldukça basittir. uzatma yöntemi için

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(Guid id, Department Model) 
{ 
    db.ApplyDetachedPropertyChanges(Model); 
    db.SaveChanges(); 
} 
İlgili konular