Bir POST eyleminde Görünüm Modeli ile Etki Alanı Modeli nasıl eşlenir?


87

İnternette ViewModels kullanımı ve Automapper'ı kullanma hakkında bulunan her makale, "Kontrolör -> Görünüm" yön eşlemesinin kılavuzlarını verir. Tüm Seçim Listeleriyle birlikte bir etki alanı modelini özelleştirilmiş bir ViewModel'e alır ve görünüme iletirsiniz. Bu açık ve güzel.
Görünümün bir formu var ve sonunda POST işlemindeyiz. Burada tüm Model Bağlayıcıları, en azından bağlama ve doğrulama adına adlandırma kurallarının bir kısmında orijinal ViewModel ile [besbelli] ilgili olan başka bir Görünüm Modeli ile birlikte sahneye çıkıyor .

Bunu Etki Alanı Modelinizle nasıl eşlersiniz?

Bir ekleme eylemi olsun, aynı Automapper'ı kullanabiliriz. Peki ya bu bir güncelleme eylemiyse? Alan Varlığımızı Depodan almalı, özelliklerini ViewModel'deki değerlere göre güncellemeli ve Depoya kaydetmeliyiz.

EK 1 (9 Şubat 2010): Bazen Modelin özelliklerini atamak yeterli olmaz. View Model değerlerine göre Domain Modeline karşı bir takım önlemler alınmalıdır. Yani, Etki Alanı Modelinde bazı yöntemler çağrılmalıdır. Muhtemelen, Modelleri Görüntüle'yi işlemek için Denetleyici ve Etki Alanı arasında duran bir tür Uygulama Hizmeti katmanı olmalıdır ...


Bu kod nasıl düzenlenir ve aşağıdaki hedeflere ulaşmak için nereye yerleştirilir?

  • denetleyicileri ince tutun
  • SoC uygulamasını onurlandırın
  • Etki Alanına Dayalı Tasarım ilkelerini takip edin
  • KURU olmak
  • devam edecek ...

Yanıtlar:


37

Bir kullanmak IBuilder arayüzünü kullanarak bunu uygulamaya ValueInjecter

public interface IBuilder<TEntity, TViewModel>
{
      TEntity BuildEntity(TViewModel viewModel);
      TViewModel BuildViewModel(TEntity entity);
      TViewModel RebuildViewModel(TViewModel viewModel); 
}

... (uygulama) RebuildViewModel sadece çağırırBuildViewModel(BuilEntity(viewModel))

[HttpPost]
public ActionResult Update(ViewModel model)
{
   if(!ModelState.IsValid)
    {
       return View(builder.RebuildViewModel(model);
    }

   service.SaveOrUpdate(builder.BuildEntity(model));
   return RedirectToAction("Index");
}

btw ViewModel yazmıyorum Giriş yazıyorum çünkü çok daha kısa, ama bu gerçekten önemli değil
umarım yardımcı olur

Güncelleme: Bu yaklaşımı şimdi ProDinner ASP.net MVC Demo Uygulamasında kullanıyorum , şimdi IMapper olarak adlandırılıyor, bu yaklaşımın ayrıntılı olarak açıklandığı bir pdf de var


Bu yaklaşımı beğendim. Yine de net olmadığım bir şey, özellikle katmanlı bir uygulama ışığında IBuilder'ın uygulanmasıdır. Örneğin, ViewModel'imin 3 Seçme Listesi var. Oluşturucu uygulaması, seçilen liste değerlerini havuzdan nasıl alır?
Matt Murrell

@Matt Murrell prodinner.codeplex.com'a bakın Bunu orada yapıyorum ve ona IBuilder yerine IMapper diyorum
Omu

6
Bu yaklaşımı beğendim, burada bir örnek uyguladım: gist.github.com/2379583
Paul Stovell

Bana göre Etki Alanı Modeli yaklaşımıyla uyumlu değil. Belirsiz gereksinimler için bazı CRUD yaklaşımı gibi görünüyor. Bazı makul eylemleri iletmek için Etki Alanı Modelinde Fabrikaları (DDD) ve ilgili yöntemleri kullanmamalı mıyız? Bu şekilde, DB'den bir varlığı yüklememiz ve gerektiği gibi güncellememiz daha iyi olur, değil mi? Yani tam olarak doğru değil gibi görünüyor.
Artyom

7

AutoMapper gibi araçlar, mevcut nesneyi kaynak nesneden gelen verilerle güncellemek için kullanılabilir. Güncelleme için denetleyici eylemi şöyle görünebilir:

[HttpPost]
public ActionResult Update(MyViewModel viewModel)
{
    MyDataModel dataModel = this.DataRepository.GetMyData(viewModel.Id);
    Mapper<MyViewModel, MyDataModel>(viewModel, dataModel);
    this.Repostitory.SaveMyData(dataModel);
    return View(viewModel);
}

Yukarıdaki ön bilgide görünenlerin dışında:

  • Modeli görüntülemek için POST verileri + doğrulaması ModelBinder'da yapılır (özel bağlamalarla genişletilebilir)
  • Hata işleme (yani Veri Havuzu tarafından veri erişim istisnası atışlarının yakalanması) [HandleError] filtresi ile yapılabilir

Denetleyici eylemi oldukça zayıftır ve endişeler birbirinden ayrılmıştır: eşleme sorunları AutoMapper yapılandırmasında ele alınır, doğrulama ModelBinder tarafından ve veri erişimi Depo tarafından yapılır.


6
Düzleştirmeyi tersine çeviremediği için Automapper'ın burada yararlı olduğundan emin değilim. Sonuçta, Etki Alanı Modeli, Görünüm Modeli gibi basit bir DTO değildir, bu nedenle ona bazı özellikler atamak yeterli olmayabilir. Muhtemelen View Model içeriğine göre Domain Modeline karşı bazı aksiyonlar yapılmalıdır. Ancak, +1 oldukça iyi bir yaklaşım paylaştığı için.
Anthony Serdyukov

@Anton ValueInjecter düzleştirmeyi tersine çevirebilir;)
Omu

bu yaklaşımla denetleyiciyi ince tutmazsınız, SoC ve DRY'yi ihlal edersiniz ... Omu'nun bahsettiği gibi, haritalama işlerini önemseyen ayrı bir katmana sahip olmanız gerekir.
Rookian

5

Müşteri etkileşiminin her iki yönü için ViewModel terimini yeniden kullandığınızı söylemek isterim. Vahşi ortamda yeterince ASP.NET MVC kodu okuduysanız, muhtemelen bir ViewModel ve bir EditModel arasındaki farkı görmüşsünüzdür. Bunun önemli olduğunu düşünüyorum.

Bir ViewModel, bir görünümü oluşturmak için gereken tüm bilgileri temsil eder. Bu, etkileşimli olmayan statik yerlerde oluşturulan verileri ve ayrıca yalnızca tam olarak neyin oluşturulacağına karar vermek için bir kontrol gerçekleştirmek için verileri içerebilir. Bir Denetleyici GET eylemi genellikle ViewModel'in Görünümü için paketlenmesinden sorumludur.

Bir EditModel (veya belki bir ActionModel), kullanıcının o POST için yapmak istediği eylemi gerçekleştirmek için gereken verileri temsil eder. Yani bir EditModel gerçekten bir eylemi tanımlamaya çalışıyor. Bu muhtemelen bazı verileri ViewModel'den hariç tutacaktır ve ilgili olmasına rağmen bunların gerçekten farklı olduklarını anlamanın önemli olduğunu düşünüyorum.

Bir Fikir

Bu, Model -> ViewModel'den gitmek için bir AutoMapper yapılandırmasına ve EditModel -> Model'e gitmek için farklı bir yapılandırmaya kolayca sahip olabileceğinizi söyledi. O zaman farklı Denetleyici eylemlerinin yalnızca AutoMapper'ı kullanması gerekir. Cehennem EditModel, modele karşı özelliklerini doğrulamak ve bu değerleri Modelin kendisine uygulamak için üzerinde işlevlere sahip olabilir. Başka bir şey yapmıyor ve yine de Talebi EditModel ile eşlemek için MVC'de ModelBinders var.

Diğer bir fikir

Bunun ötesinde, son zamanlarda bir ActionModel fikrinden yola çıkarak düşündüğüm bir şey de, müşterinin size geri gönderdiği şeyin aslında kullanıcının gerçekleştirdiği birkaç eylemin açıklaması ve yalnızca bir veri yığını değil. Bu kesinlikle istemci tarafında yönetmek için biraz Javascript gerektirecektir, ancak fikir bence ilgi çekici.

Esasen, kullanıcı sunduğunuz ekranda eylemler gerçekleştirirken, Javascript bir eylem nesneleri listesi oluşturmaya başlayacaktır. Bir örnek, muhtemelen kullanıcı bir çalışan bilgi ekranındadır. Çalışan yakın zamanda evlendiği için soyadını günceller ve yeni bir adres eklerler. Kapakların altında bu, bir listeye bir ChangeEmployeeNameve bir AddEmployeeMailingAddressnesne oluşturur. Kullanıcı değişiklikleri uygulamak için 'Kaydet'i tıklar ve her biri yalnızca her eylemi gerçekleştirmek için gereken bilgileri içeren iki nesnenin listesini gönderirsiniz.

Daha akıllı bir ModelBinder'e ihtiyacınız olacaktır, bu durumda varsayılan olanı ancak iyi JSON serileştiricisi, istemci tarafı eylem nesnelerinin sunucu tarafındaki nesnelerle eşleştirilmesini sağlayabilmelidir. Sunucu tarafı olanlar (2 katmanlı bir ortamdaysanız), birlikte çalıştıkları Model üzerindeki eylemi tamamlayan yöntemlere kolayca sahip olabilir. Dolayısıyla, Denetleyici eylemi, yalnızca Model örneğinin çekmesi için bir Kimlik ve üzerinde gerçekleştirilecek eylemlerin bir listesini alır. Veya eylemlerin içinde onları çok ayrı tutacak bir kimlik vardır.

Yani belki sunucu tarafında bunun gibi bir şey gerçekleşebilir:

public interface IUserAction<TModel>
{
     long ModelId { get; set; }
     IEnumerable<string> Validate(TModel model);
     void Complete(TModel model);
}

[Transaction] //just assuming some sort of 2-tier with transactions handled by filter
public ActionResult Save(IEnumerable<IUserAction<Employee>> actions)
{
     var errors = new List<string>();
     foreach( var action in actions ) 
     {
         // relying on ORM's identity map to prevent multiple database hits
         var employee = _employeeRepository.Get(action.ModelId);
         errors.AddRange(action.Validate(employee));
     }

     // handle error cases possibly rendering view with them

     foreach( var action in editModel.UserActions )
     {
         var employee = _employeeRepository.Get(action.ModelId);
         action.Complete(employee);
         // against relying on ORMs ability to properly generate SQL and batch changes
         _employeeRepository.Update(employee);
     }

     // render the success view
}

Bu, geri gönderme eylemini gerçekten oldukça genel kılar, çünkü size doğru IUserAction örneğini ve IUserAction örneğinizi doğru mantığı gerçekleştirmek veya (daha büyük olasılıkla) bilgi ile Modele çağrı yapmak için size güveniyorsunuz.

Eğer 3 katmanlı bir ortamda olsaydınız, IUserAction sadece basit DTO'lar haline getirilebilir ve sınırın ötesinde çekilebilir ve uygulama katmanında benzer bir yöntemle gerçekleştirilebilir. Bu katmanı nasıl yaptığınıza bağlı olarak, çok kolay bir şekilde bölünebilir ve yine de bir işlemde kalabilir (akla gelen, Agatha'nın isteği / yanıtı ve DI ve NHibernate'in kimlik haritasından yararlanılmasıdır).

Her neyse, bunun mükemmel bir fikir olmadığından eminim, müşteri tarafında yönetmek için biraz JS gerektirecekti ve nasıl gelişeceğini görmek için henüz bir proje yapamadım, ancak gönderi nasıl yapılacağını düşünmeye çalışıyordu oraya git ve tekrar geri dön, böylece düşüncelerimi vereceğimi düşündüm. Umarım yardımcı olur ve etkileşimleri yönetmenin başka yollarını duymak isterim.


İlginç. ViewModel ve EditModel arasındaki ayrımla ilgili olarak ... bir düzenleme işlevi için formu oluşturmak için bir ViewModel kullanmanızı ve ardından kullanıcı bunu yayınladığında bir EditModel'e bağlanmanızı mı öneriyorsunuz? Öyleyse, doğrulama hataları nedeniyle formu yeniden göndermeniz gereken durumlarla nasıl başa çıkardınız (örneğin, ViewModel bir açılır listeyi dolduracak öğeler içerdiğinde) - açılır öğeleri de EditModel'e dahil eder miydiniz? Hangi durumda ikisi arasındaki fark ne olur?
UpTheCreek

Tahmin ediyorum ki, bir EditModel kullanırsam ve bir hata oluşursa, ViewModel'imi yeniden inşa etmem gerekir ki bu çok pahalı olabilir. ViewModel'i yeniden oluşturduğundan ve kullanıcı bildirim mesajlarını (muhtemelen doğrulama hataları gibi hem olumlu hem de olumsuz olanlar) koyabileceği bir yere sahip olduğundan emin olun derim. Bir performans sorunu olduğu ortaya çıkarsa, o oturumun bir sonraki isteği sona erene kadar (muhtemelen EditModel'in postası olan) ViewModel'i her zaman önbelleğe alabilirsiniz.
Sean Copenhaver

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.