ViewModel En İyi Uygulamaları


238

Gönderen bu soruya , bir kontrolör bir oluşturmasını sağlamak için mantıklı gibi görünüyor ViewModel daha doğru görünümü ekrana çalıştığı modelini yansıtan, ama (ben MVC deseni yeniyim sözleşmelerin bazı merak ediyorum , zaten belli değilse).

Temel olarak, aşağıdaki sorularım vardı:

  1. Normalde bir sınıf / dosyaya sahip olmayı severim. Yalnızca bir denetleyiciden görünüme veri dağıtmak için oluşturuluyorsa , bu bir ViewModel ile anlamlı mı ?
  2. Bir ViewModel kendi dosyasına aitse ve işleri ayrı tutmak için bir dizin / proje yapısı kullanıyorsanız, ViewModel dosyası nereye aittir? Gelen Kontrolörler dizinine?

Şimdilik bu kadar. Birkaç sorum daha olabilir, ama bu son bir saattir beni rahatsız ediyor ve başka yerlerde tutarlı rehberlik bulabilirim.

EDIT: CodePlex üzerindeki örnek NerdDinner uygulamasına baktığımızda , ViewModels Kontrolörlerin bir parçası gibi görünüyor , ancak yine de kendi dosyalarında olmadıkları beni rahatsız ediyor.


66
NerdDinner'a tam olarak "En İyi Uygulamalar" örneği demezdim. Sezginiz size iyi hizmet eder. :)
Ryan Montgomery

Yanıtlar:


211

Her görünüm için "ViewModel" dediğim şeyi oluşturuyorum. Onları MVC Web projemde ViewModels adlı bir klasöre koydum. Bunları, temsil ettikleri denetleyiciden ve eylemden (veya görünümden) sonra adlandırıyorum. Üyelik denetleyicisinde SignUp görünümüne veri iletmek gerekirse Yani bir MembershipSignUpViewModel.cs sınıfı oluşturmak ve ViewModels klasörüne koymak.

Sonra verilerin denetleyiciden görünüme aktarılmasını kolaylaştırmak için gerekli özellikleri ve yöntemleri ekliyorum. ViewModel'imden Etki Alanı Modeline gitmek için Automapper'ı kullanıyorum ve gerekirse tekrar geri dönüyorum.

Bu, diğer ViewModels türündeki özellikleri içeren bileşik ViewModels için de iyi çalışır. Örneğin, üyelik denetleyicisindeki dizin sayfasında 5 pencere öğeniz varsa ve her kısmi görünüm için bir ViewModel oluşturduysanız - verileri Dizin eyleminden kısmi öğelere nasıl geçirirsiniz? MyPartialViewModel türünde MembershipIndexViewModel'e bir özellik eklersiniz ve kısmi oluştururken Model.MyPartialViewModel'e geçersiniz.

Bu şekilde yapmak, Dizin görünümünü hiç değiştirmek zorunda kalmadan kısmi ViewModel özelliklerini ayarlamanıza olanak tanır. Hala Model.MyPartialViewModel'de geçer, bu yüzden yaptığınız tek şey kısmi ViewModel'e bir özellik eklerken bir şeyleri düzeltmek için tüm kısmi zincirinden geçme şansınız daha azdır.

Ayrıca "MyProject.Web.ViewModels" ad alanını, her görünümde açık bir içe aktarma ifadesi eklemeden herhangi bir görünümde referans verebilmem için web.config dosyasına ekleyeceğim. Sadece biraz daha temiz yapar.


3
Kısmi bir görünümden POST yapmak ve tüm görünümü döndürmek istiyorsanız (model hatası durumunda) ne olur? Kısmi görünümde ana modele erişemezsiniz.
Cosmo

5
@ Cosmo: Sonra bir model hatası durumunda tüm görünümü döndürebilecek bir eyleme POST . Sunucu tarafında, üst modeli yeniden oluşturmak için yeterli paranız var.
Tomas Aschan

Bir giriş [POST] ve giriş [GET] işlemlerine ne dersiniz? farklı görünüm modelleri ile?
Bart Calixto

Genellikle giriş [GET], ViewModel'i çağırmaz, çünkü herhangi bir veri yüklemeniz gerekmez.
Andre Figueiredo

Harika tavsiye. Model / VM özelliklerinin veri erişimi, işlenmesi ve ayarlanması nereye gitmelidir? Benim durumumda, yerel bir CMS veritabanından gelen bazı verilerimiz ve web servislerinden gelen bazı verilerimiz olacak, bu da bir modele ayarlanmadan önce işlenmesi / değiştirilmesi gerekir. Tüm bunları denetleyiciye koymak oldukça dağınık oluyor.
xr280xr

124

Sınıfları kategorilere göre ayırmak (Denetleyiciler, ViewModels, Filtreler vb.) Saçmadır.

Web sitenizin Giriş bölümü için kod yazmak istiyorsanız (/), Home adlı bir klasör oluşturun ve HomeController, IndexViewModel, AboutViewModel vb. Ve Home eylemleri tarafından kullanılan tüm ilgili sınıfları buraya koyun.

ApplicationController gibi paylaşılan sınıflarınız varsa, bunu projenizin kök dizinine koyabilirsiniz.

Neden ilişkili (HomeController, IndexViewModel) şeyleri ayırın ve hiç bir ilişkisi olmayan şeyleri bir arada tutun (HomeController, AccountController)?


Bu konuyla ilgili bir blog yazısı yazdım .


13
Bunu yaparsan her şey çok çabuk dağınık olacak.
UpTheCreek

14
Hayır, dağınık tüm denetleyicileri tek bir dir / ad alanına koymaktır. Her biri 5 görünüm modeli kullanan 5 denetleyiciniz varsa, 25 görünüm modeliniz vardır. Ad alanları, kodu düzenleme mekanizmasıdır ve burada farklı olmamalıdır.
Max Toro

41
@Max Toro: Çok fazla indirildiğine şaşırdım. ASP.Net MVC üzerinde bir süre çalıştıktan sonra, tüm ViewModels'ları bir yerde, tüm denetleyicileri başka bir yerde ve tüm Görünümleri başka bir yerde bulundurmaktan çok acı çekiyorum . MVC, ilgili parçaların üçlüsüdür , birleştirilirler - birbirlerini desteklerler. Belirli bir bölüm için Kontrolör, ViewModels ve Görünümler aynı dizinde birlikte yaşıyorsa bir çözüm çok daha organize olabilir gibi hissediyorum . Uygulamam / Hesaplar / Denetleyici.cs, Uygulamam / Hesaplar / Oluştur / ViewModel.cs, Uygulamam / Hesaplar / Oluştur / Görünüm.cshtml, vb.
quentin-

13
Endişelerin ayrılması sınıfların ayrılması değildir.
Max Toro

12
@RyanJMcGowan, gelişime nasıl yaklaşırsanız çalışın, özellikle büyük uygulamalar için sorunla sonuçlandığınız şeydir. Bakım modundayken, tüm denetleyicileri ve sonra tüm denetleyicileri düşünmezsiniz, her seferinde bir işlev eklersiniz.
Max Toro

21

Uygulama sınıflarımı "Core" (veya ayrı bir sınıf kütüphanesi) adlı bir alt klasörde tutuyorum ve KIGG örnek uygulamasıyla aynı yöntemleri kullanıyorum ancak uygulamalarımı daha KURU yapmak için bazı küçük değişiklikler yapıyorum.

Ortak bir site genelinde özellikleri depolamak / Core / ViewData / içinde bir BaseViewData sınıfı oluşturmak.

Bundan sonra aynı zamanda BaseViewData türetmek ve görünüm belirli özelliklere sahip aynı klasörde tüm görünüm ViewData sınıfları oluşturmak.

Sonra tüm denetleyicilerim türetilmiş bir ApplicationController oluşturun. ApplicationController aşağıdaki gibi genel bir GetViewData yöntemine sahiptir:

protected T GetViewData<T>() where T : BaseViewData, new()
    {
        var viewData = new T
        {
           Property1 = "value1",
           Property2 = this.Method() // in the ApplicationController
        };
        return viewData;
    }

Son olarak, Denetleyici eylemimde ViewData Modelimi oluşturmak için aşağıdakileri yapıyorum

public ActionResult Index(int? id)
    {
        var viewData = this.GetViewData<PageViewData>();
        viewData.Page = this.DataContext.getPage(id); // ApplicationController
        ViewData.Model = viewData;
        return View();
    }

Bu gerçekten iyi çalışıyor ve görüşlerinizi düzenli ve denetleyicilerinizi sıska tutar.


13

ViewModel sınıfı, sınıf örnekleri tarafından temsil edilen birden çok veri parçasını Görünümünüze iletebileceğiniz, yönetmesi kolay tek bir nesneye kapsüllemek için vardır.

ViewModel sınıflarınızın kendi dosyalarında, kendi dizinlerinde bulunması mantıklı olacaktır. Projelerimde, Modeller klasörünün ViewModels adında bir alt klasörü var. ViewModeller'im (ör. ProductViewModel.cs) Burada yaşıyor.


13

Modellerinizi saklamak için iyi bir yer yoktur. Proje büyükse ve çok sayıda ViewModels (Veri Aktarım Nesneleri) varsa bunları ayrı bir montajda tutabilirsiniz. Ayrıca bunları site projesinin ayrı bir klasöründe tutabilirsiniz. Örneğin, Oxite'de çok çeşitli sınıflar içeren Oxite projesine yerleştirilirler. Oxite kontrolörleri ayrı bir projeye taşınır ve görüşler de ayrı bir projede bulunur.
In CodeCampServer ViewModels * Formunu adlandırılır ve onlar Modelleri klasöründe UI projesinde yerleştirilir.
In MvcPress projesi aynı zamanda veritabanı ve biraz daha çalışmak için tüm kodu içeren veri projesi yerleştirilir (ancak bu yaklaşımı tavsiye etmedi, bir örnek için sadece var)
Gördüğünüz gibi birçok bakış açısı var. ViewModels'imi (DTO nesneleri) genellikle site projesinde tutarım. Ancak 10'dan fazla modelim olduğunda bunları ayrı bir montaja taşımayı tercih ederim. Genellikle bu durumda kontrolörleri de montajı ayırmak için hareket ettiriyorum.
Başka bir soru, modeldeki tüm verilerin ViewModel'inize nasıl kolayca eşleneceğidir. AutoMapper kütüphanesine bakmanızı öneririm . Çok hoşuma gitti, benim için tüm kirli işleri yapıyor.
Ayrıca SharpArchitecture projesine bakmanızı öneririm . Projeler için çok iyi bir mimari sağlar ve birçok harika çerçeve, rehberlik ve harika topluluk içerir.


8
ViewModels! = DTO
Bart Calixto

6

İşte en iyi uygulamalarımdan bir kod snippet'i:

    public class UserController : Controller
    {
        private readonly IUserService userService;
        private readonly IBuilder<User, UserCreateInput> createBuilder;
        private readonly IBuilder<User, UserEditInput> editBuilder;

        public UserController(IUserService userService, IBuilder<User, UserCreateInput> createBuilder, IBuilder<User, UserEditInput> editBuilder)
        {
            this.userService = userService;
            this.editBuilder = editBuilder;
            this.createBuilder = createBuilder;
        }

        public ActionResult Index(int? page)
        {
            return View(userService.GetPage(page ?? 1, 5));
        }

        public ActionResult Create()
        {
            return View(createBuilder.BuildInput(new User()));
        }

        [HttpPost]
        public ActionResult Create(UserCreateInput input)
        {
            if (input.Roles == null) ModelState.AddModelError("roles", "selectati macar un rol");

            if (!ModelState.IsValid)
                return View(createBuilder.RebuildInput(input));

            userService.Create(createBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }

        public ActionResult Edit(long id)
        {
            return View(editBuilder.BuildInput(userService.GetFull(id)));
        }

        [HttpPost]
        public ActionResult Edit(UserEditInput input)
        {           
            if (!ModelState.IsValid)
                return View(editBuilder.RebuildInput(input));

            userService.Save(editBuilder.BuilEntity(input));
            return RedirectToAction("Index");
        }
}

5

Tüm ViewModeller'imizi Modeller klasörüne atıyoruz (tüm iş mantığımız ayrı bir ServiceLayer projesinde)


4

Şahsen ben ViewModel önemsiz dışında bir şey olup olmadığını öneririz sonra ayrı bir sınıf kullanın.

Birden fazla görünüm modeliniz varsa, en azından bir dizinde bölümlendirmenin mantıklı olmasını öneririm. görünüm modeli daha sonra paylaşılırsa, dizinde belirtilen ad alanı yeni bir derlemeye taşınmayı kolaylaştırır.


2

Bizim durumumuzda, Görünümlerden ayrı bir projede Denetleyicilerle birlikte Modellerimiz var.

Genel bir kural olarak, ViewData ["..."] öğelerinin çoğunu ViewModel'e taşımaya ve bunlardan kaçınmaya çalıştık, bu nedenle dökümler ve sihirli dizelerden kaçıyoruz, bu iyi bir şey.

ViewModel, liste kırıntıları ve başlıkları çizmek için sayfalar için sayfalama bilgileri veya sayfanın başlık bilgileri gibi bazı ortak özelliklere de sahiptir. Şu anda baz sınıf benim görüşüme göre çok fazla bilgi barındırıyor ve bunu üç parçaya bölebiliriz, bir temel görünüm modelindeki sayfaların% 99'u için en temel ve gerekli bilgi ve daha sonra listeler ve bir model için bir model bu senaryolar için belirli verileri tutan ve temel senaryodan devralınan formlar için.

Son olarak, her bir varlığın belirli bilgileri ele alması için bir görünüm modeli uyguluyoruz.


0

denetleyicideki kod:

    [HttpGet]
        public ActionResult EntryEdit(int? entryId)
        {
            ViewData["BodyClass"] = "page-entryEdit";
            EntryEditViewModel viewMode = new EntryEditViewModel(entryId);
            return View(viewMode);
        }

    [HttpPost]
    public ActionResult EntryEdit(Entry entry)
    {
        ViewData["BodyClass"] = "page-entryEdit";            

        #region save

        if (ModelState.IsValid)
        {
            if (EntryManager.Update(entry) == 1)
            {
                return RedirectToAction("EntryEditSuccess", "Dictionary");
            }
            else
            {
                return RedirectToAction("EntryEditFailed", "Dictionary");
            }
        }
        else
        {
            EntryEditViewModel viewModel = new EntryEditViewModel(entry);
            return View(viewModel);
        }

        #endregion
    }

görünüm modelindeki kod:

public class EntryEditViewModel
    {
        #region Private Variables for Properties

        private Entry _entry = new Entry();
        private StatusList _statusList = new StatusList();        

        #endregion

        #region Public Properties

        public Entry Entry
        {
            get { return _entry; }
            set { _entry = value; }
        }

        public StatusList StatusList
        {
            get { return _statusList; }
        }

        #endregion

        #region constructor(s)

        /// <summary>
        /// for Get action
        /// </summary>
        /// <param name="entryId"></param>
        public EntryEditViewModel(int? entryId)
        {
            this.Entry = EntryManager.GetDetail(entryId.Value);                 
        }

        /// <summary>
        /// for Post action
        /// </summary>
        /// <param name="entry"></param>
        public EntryEditViewModel(Entry entry)
        {
            this.Entry = entry;
        }

        #endregion       
    }

projeleri:

  • DevJet.Web (ASP.NET MVC web projesi)

  • DevJet.Web.App.Dictionary (ayrı bir Sınıf Kütüphanesi projesi)

    Bu projede, aşağıdaki gibi bazı klasörler yaptım: DAL, BLL, BO, VM (görünüm modelleri için klasör)


Merhaba, Giriş sınıfının yapısını paylaşır mısınız?
Dinis Cruz

0

İşlem ve bağlamsal veriler gibi yaygın olarak gerekli özelliklere sahip bir görünüm modeli temel sınıfı oluşturun, ayrıca geçerli kullanıcı verilerini ve rolleri de koyabilirsiniz

class ViewModelBase 
{
  public bool HasError {get;set;} 
  public string ErrorMessage {get;set;}
  public List<string> UserRoles{get;set;}
}

Temel denetleyici sınıfında PopulateViewModelBase () gibi bir yöntem var, bu yöntem bağlamsal verileri ve kullanıcı rollerini dolduracaktır. HasError ve ErrorMessage, service / db'den veri alırken bir istisna varsa bu özellikleri ayarlayın. Hatayı göstermek için bu özellikleri görünümde bağlayın. Kullanıcı rolleri, rollere dayalı görünümde gizleme bölümünü göstermek için kullanılabilir.

Farklı get eylemlerinde görünüm modellerini doldurmak için, FillModel soyut yöntemiyle temel denetleyiciye sahip olmasıyla tutarlı hale getirilebilir

class BaseController :BaseController 
{
   public PopulateViewModelBase(ViewModelBase model) 
{
   //fill up common data. 
}
abstract ViewModelBase FillModel();
}

Kontrolörlerde

class MyController :Controller 
{

 public ActionResult Index() 
{
   return View(FillModel()); 
}

ViewModelBase FillModel() 
{ 
    ViewModelBase  model=;
    string currentAction = HttpContext.Current.Request.RequestContext.RouteData.Values["action"].ToString(); 
 try 
{ 
   switch(currentAction) 
{  
   case "Index": 
   model= GetCustomerData(); 
   break;
   // fill model logic for other actions 
}
}
catch(Exception ex) 
{
   model.HasError=true;
   model.ErrorMessage=ex.Message;
}
//fill common properties 
base.PopulateViewModelBase(model);
return model;
}
}
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.