ViewModel सर्वश्रेष्ठ अभ्यास


238

से इस सवाल का , ऐसा लगता है कि यह समझ में आता है एक नियंत्रक एक बनाने के लिए ViewModel कि अधिक सही मॉडल उस दृश्य प्रदर्शित करने के लिए कोशिश कर रहा है को दर्शाता है, लेकिन मैं सम्मेलनों में से कुछ के बारे में उत्सुक (मैं MVC पैटर्न के लिए नया हूं , अगर यह पहले से ही स्पष्ट नहीं था)।

मूल रूप से, मेरे पास निम्नलिखित प्रश्न थे:

  1. मैं आम तौर पर एक वर्ग / फ़ाइल रखना पसंद करता हूं। क्या यह एक ViewModel के साथ समझ में आता है अगर यह केवल नियंत्रक से डेटा को देखने के लिए बनाया जा रहा है?
  2. यदि कोई ViewModel अपनी फ़ाइल में है, और आप एक निर्देशिका / प्रोजेक्ट संरचना का उपयोग कर रहे हैं, तो चीजों को अलग रखने के लिए, ViewModel फ़ाइल कहाँ से संबंधित है? में नियंत्रकों निर्देशिका?

यह मूल रूप से अभी के लिए है। मेरे कुछ और सवाल हो सकते हैं, लेकिन यह मुझे पिछले एक-एक घंटे से परेशान कर रहा है, और मुझे कहीं और लगातार मार्गदर्शन मिल सकता है।

EDIT: CodePlex पर नमूना NerdDinner ऐप को देखते हुए , ऐसा लगता है कि ViewModels नियंत्रकों का हिस्सा है , लेकिन यह अभी भी मुझे असहज बनाता है कि वे अपनी फाइलों में नहीं हैं।


66
मैं वास्तव में NerdDinner को "सर्वश्रेष्ठ आचरण" उदाहरण नहीं कहूँगा। आपका अंतर्ज्ञान आपको अच्छी तरह से परोसता है। :)
रयान मॉन्टगोमरी

जवाबों:


211

मैं वह बनाता हूं जिसे मैं प्रत्येक दृश्य के लिए "ViewModel" कहता हूं। मैंने उन्हें अपने MVC वेब प्रोजेक्ट में ViewModels नामक फ़ोल्डर में रखा। मैं उन्हें नियंत्रक और कार्रवाई (या दृश्य) के नाम पर बताता हूं जो वे प्रतिनिधित्व करते हैं। इसलिए अगर मुझे सदस्यता नियंत्रक पर मैं साइनअप दृश्य को डेटा पास करने की आवश्यकता है तो मैं एक सदस्यताशिप संयुक्तांक ViewModel.cs वर्ग बनाता हूं और इसे ViewModels फ़ोल्डर में डाल देता हूं।

फिर मैं नियंत्रक से दृश्य में डेटा के हस्तांतरण की सुविधा के लिए आवश्यक गुण और विधियाँ जोड़ता हूं। मैं अपने ViewModel से डोमेन मॉडल प्राप्त करने के लिए ऑटोमैपर का उपयोग करता हूं और यदि आवश्यक हो तो फिर से वापस करता हूं।

यह समग्र ViewModels के लिए भी अच्छा काम करता है जिसमें ऐसे गुण होते हैं जो अन्य ViewModels के प्रकार के होते हैं। उदाहरण के लिए यदि आपके पास सदस्यता नियंत्रक में अनुक्रमणिका पृष्ठ पर 5 विजेट हैं, और आपने प्रत्येक आंशिक दृश्य के लिए एक ViewModel बनाया है - तो आप अनुक्रमणिका क्रिया से डेटा को कैसे विभेदित करते हैं? आप टाइप करें MyPartialViewModel के सदस्यता संख्या में एक संपत्ति जोड़ते हैं और जब आप प्रतिपादन करते हैं तो आप Model.MyPartialViewModel में पास हो जाएंगे।

इसे इस तरह से करने से आप आंशिक ViewModel गुणों को समायोजित कर सकते हैं, बिना इंडेक्स दृश्य को बदलने के। यह अभी भी सिर्फ Model.MyPartialViewModel में ही गुजरता है, इसलिए इस बात की संभावना कम है कि जब आप कर रहे हैं तो कुछ को ठीक करने के लिए आपको आंशिक सीरीज़ की पूरी श्रृंखला से गुजरना होगा।

मैं web.config के लिए "MyProject.Web.ViewModels" नाम भी जोड़ूंगा ताकि मुझे किसी भी दृश्य में उन्हें संदर्भित करने की अनुमति मिले बिना प्रत्येक दृश्य पर एक स्पष्ट आयात विवरण जोड़ सके। बस इसे थोड़ा क्लीनर बनाता है।


3
यदि आप आंशिक दृष्टिकोण से POST करना चाहते हैं और संपूर्ण दृश्य (मॉडल त्रुटि के मामले में) वापस चाहते हैं तो क्या होगा? आंशिक दृश्य के भीतर आपके पास मूल मॉडल तक पहुंच नहीं है।
कॉस्मो

5
@Cosmo: फिर एक ऐसी कार्रवाई के लिए POST जो एक मॉडल त्रुटि के मामले में पूरे दृश्य को वापस कर सकती है। सर्वर की ओर, आपके पास मूल मॉडल को फिर से बनाने के लिए पर्याप्त है।
टॉमस असचन

लॉगिन [POST] और लॉगिन [GET] क्रियाओं के बारे में क्या? अलग-अलग दृष्टिकोण के साथ?
बार्ट कैलिक्सो

आमतौर पर, लॉगिन [प्राप्त करें] ViewModel को कॉल न करें क्योंकि किसी भी डेटा को लोड करने की आवश्यकता नहीं है।
आंद्रे फिग्यूएरेडो

अच्छी सलाह। मॉडल / वीएम संपत्तियों की डेटा एक्सेस, प्रोसेसिंग और सेटिंग कहां जानी चाहिए? मेरे मामले में हमारे पास स्थानीय सीएमएस डेटाबेस से आने वाले कुछ डेटा और वेब सेवाओं से आने वाले कुछ डेटा होंगे, जिन्हें मॉडल पर सेट होने से पहले संसाधित / हेरफेर करना होगा। कंट्रोलर में वह सब डाल देना बहुत गन्दा हो जाता है।
xr280xr

124

श्रेणी (नियंत्रक, ViewModels, फ़िल्टर आदि) द्वारा अलग-अलग कक्षाएं बकवास है।

अगर आप अपनी वेबसाइट के होम सेक्शन (/) के लिए कोड लिखना चाहते हैं तो होम नाम का एक फोल्डर बनाएं, और वहां होमकंट्रोलर, इंडेक्स व्यूमॉडल, अबाउटमॉडल इत्यादि और होम एक्शन द्वारा उपयोग किए जाने वाले सभी संबंधित वर्गों को डाल दें।

यदि आपके पास एक साझा अनुप्रयोग है, तो ApplicationController की तरह, आप इसे अपनी परियोजना की जड़ में रख सकते हैं।

क्यों अलग-अलग चीजें जो संबंधित हैं (HomeController, IndexViewModel) और उन चीजों को एक साथ रखें जिनका कोई संबंध नहीं है (HomeController, AccountController)?


मैंने इस विषय पर एक ब्लॉग पोस्ट लिखी ।


13
यदि आप ऐसा करते हैं तो चीजें बहुत गड़बड़ हो जाती हैं।
UpTheCreek

14
नहीं, गन्दा सभी नियंत्रकों को एक dir / namepace में रखना है। यदि आपके पास 5 कंट्रोलर हैं, प्रत्येक में 5 व्यूमॉडल का उपयोग किया जाता है, तो आपको 25 व्यूमॉडल मिल गए हैं। Namespaces कोड को व्यवस्थित करने के लिए एक तंत्र है, और यहाँ कोई अलग नहीं होना चाहिए।
मैक्स टोरो

41
@ मोम टोरो: आश्चर्य है कि आप इतना निराश हो गए। कुछ समय के बाद ASP.Net MVC पर काम कर रहा है, मैं एक महसूस कर रहा हूँ बहुत कुछ होने से दर्द के सभी , एक ही स्थान पर ViewModels सब किसी अन्य रूप में नियंत्रकों, और सभी में अभी तक एक और दृश्य। MVC, संबंधित टुकड़े की तिकड़ी है वे कर रहे हैं युग्मित - वे एक दूसरे का समर्थन। मुझे लगता है कि एक समाधान मुझे और अधिक संगठित कर सकता है यदि नियंत्रक, ViewModels, और दिए गए अनुभाग के लिए दृश्य एक ही निर्देशिका में एक साथ रहते हैं। MyApp / लेखा / Controller.cs, MyApp / लेखा / बनाएं / ViewModel.cs, MyApp / लेखा / बनाएं / View.cshtml, आदि
क्वेंटिन-starin

13
@RyanJMcGowan चिंताओं का पृथक्करण वर्गों का पृथक्करण नहीं है।
मैक्स टोरो

12
@RyanJMcGowan कोई फर्क नहीं पड़ता कि आप विकास से कैसे संपर्क करते हैं, जो आप के साथ है, विशेष रूप से बड़े ऐप के लिए। एक बार रखरखाव मोड में आप सभी मॉडलों के बारे में नहीं सोचते हैं तो सभी नियंत्रक, आप एक बार में एक फ़ंक्शन जोड़ते हैं।
मैक्स टोरो

21

मैं अपने एप्लिकेशन क्लासेस को "Core" (या एक अलग श्रेणी की लाइब्रेरी) नामक एक सब फ़ोल्डर में रखता हूं और KIB के समान विधियों का उपयोग करता हूं नमूना एप्लिकेशन लेकिन मेरे अनुप्रयोगों को अधिक DRY करने के लिए कुछ थोड़े बदलाव के साथ।

मैं / Core / ViewData / जहाँ मैं आम साइट व्यापक गुण संग्रहीत करता हूँ, में एक BaseViewData वर्ग बनाता हूँ।

इसके बाद मैं अपने सभी व्यू व्यूडा क्लासेज को उसी फोल्डर में बनाता हूं, जो बाद में बेस व्यूडाटा से निकलता है और विशिष्ट गुण देखता है।

फिर मैं एक ApplicationController बनाता हूं जो मेरे सभी नियंत्रकों से प्राप्त होता है। ApplicationController एक सामान्य GetViewData विधि इस प्रकार है:

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

अंत में, मेरी नियंत्रक कार्रवाई में मैं अपना ViewData मॉडल बनाने के लिए निम्न कार्य करता हूं

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

मुझे लगता है कि यह वास्तव में अच्छी तरह से काम करता है और यह आपके विचारों को चुस्त और आपके नियंत्रकों को पतला रखता है।


13

एक ViewModel वर्ग ऑब्जेक्ट का प्रबंधन करने के लिए एक आसान में कक्षाओं के उदाहरणों द्वारा दर्शाए गए डेटा के कई टुकड़ों को एनकैप करने के लिए है जिसे आप अपने व्यू में पास कर सकते हैं।

अपनी निर्देशिका में अपनी ViewModel कक्षाओं को अपनी निर्देशिका में रखने से समझ में आएगा। मेरी परियोजनाओं में मेरे पास ViewModels नामक मॉडल फ़ोल्डर का उप-फ़ोल्डर है। यहीं मेरा ViewModels (उदा ProductViewModel.cs) रहता है।


13

आपके मॉडल को रखने के लिए कोई अच्छी जगह नहीं है। यदि परियोजना बड़ी है और आप बहुत सारे ViewModels (डेटा ट्रांसफर ऑब्जेक्ट्स) हैं, तो आप उन्हें अलग-अलग असेंबली में रख सकते हैं। इसके अलावा, आप उन्हें साइट परियोजना के अलग फ़ोल्डर में रख सकते हैं। उदाहरण के लिए, ऑक्साइट में उन्हें ऑक्साइट परियोजना में रखा जाता है, जिसमें विभिन्न वर्ग भी होते हैं। ऑक्सीटेट में नियंत्रकों को अलग परियोजना में ले जाया जाता है और दृश्य भी अलग परियोजना में होते हैं।
में CodeCampServer ViewModels * पर्चा नाम हैं और वे मॉडल फ़ोल्डर में यूआई परियोजना में रखा जाता है।
में MvcPress परियोजना वे डेटा प्रोजेक्ट है, जो भी डेटाबेस और थोड़ा अधिक के साथ काम करने के लिए सभी कोड शामिल में रखा जाता है (लेकिन मैं इस दृष्टिकोण की सिफारिश नहीं की है, यह एक नमूना के लिए बस है)
तो आप देख सकते हैं कि कई दृष्टिकोण हैं। मैं आमतौर पर अपने ViewModels (DTO ऑब्जेक्ट) को साइट प्रोजेक्ट में रखता हूं। लेकिन जब मेरे पास 10 से अधिक मॉडल हैं, तो मैं उन्हें अलग-अलग विधानसभा में स्थानांतरित करना पसंद करता हूं। आमतौर पर इस मामले में मैं असेंबली को अलग करने के लिए नियंत्रक भी ले जा रहा हूं।
एक और सवाल यह है कि मॉडल से अपने ViewModel के सभी डेटा को आसानी से कैसे मैप किया जाए। मेरा सुझाव है कि AutoMapper पुस्तकालय पर एक नज़र डालें । मुझे यह बहुत पसंद है, यह मेरे लिए सभी गंदे काम करता है।
और मैं भी SharpArchitecture प्रोजेक्ट को देखने का सुझाव देता हूं । यह परियोजनाओं के लिए बहुत अच्छी वास्तुकला प्रदान करता है और इसमें बहुत सारे शांत ढांचे और दिशानिर्देश और महान समुदाय शामिल हैं।


8
ViewModels! = डीटीओ
बार्ट कैलिक्सो

6

यहाँ मेरी सर्वोत्तम प्रथाओं में से एक कोड स्निपेट है:

    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

हम अपने सभी ViewModels को मॉडल फ़ोल्डर में फेंक देते हैं (हमारे सभी व्यावसायिक तर्क एक अलग ServiceLayer प्रोजेक्ट में हैं)


4

व्यक्तिगत रूप से मेरा सुझाव है कि अगर ViewModel कुछ भी है लेकिन तुच्छ है तो एक अलग वर्ग का उपयोग करें।

यदि आपके पास एक से अधिक दृश्य मॉडल हैं, तो मेरा सुझाव है कि इसे कम से कम एक निर्देशिका में विभाजित करने के लिए समझ में आता है। अगर बाद में दृश्य मॉडल साझा किया जाता है तो निर्देशिका में निहित नाम स्थान एक नई विधानसभा में स्थानांतरित करना आसान बनाता है।


2

हमारे मामले में हमारे पास व्यू से अलग एक परियोजना में नियंत्रकों के साथ मॉडल हैं।

अंगूठे के एक नियम के रूप में, हमने ViewMatael में अधिकतर ViewData ["..."] को स्थानांतरित करने और उससे बचने की कोशिश की है, इसलिए हम कास्टिंग और जादू के तार से बचते हैं, जो एक अच्छी बात है।

ViewModel ब्रेडक्रंब और शीर्षकों को खींचने के लिए पृष्ठ की सूचियों या हेडर सूचनाओं के लिए पृष्ठांकन जानकारी जैसी कुछ सामान्य गुणों को रखता है। इस समय बेस क्लास मेरे विचार में बहुत अधिक जानकारी रखता है और हम इसे तीन टुकड़ों में विभाजित कर सकते हैं, बेस व्यू मॉडल पर 99% पृष्ठों के लिए सबसे बुनियादी और आवश्यक जानकारी, और फिर सूचियों और एक मॉडल के लिए एक मॉडल उन रूपों के लिए जो उस परिदृश्य के लिए विशिष्ट डेटा रखते हैं और बेस एक से विरासत में मिलते हैं।

अंत में, हम विशिष्ट जानकारी से निपटने के लिए प्रत्येक इकाई के लिए एक दृश्य मॉडल लागू करते हैं।


0

नियंत्रक में कोड:

    [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
    }

दृश्य मॉडल में कोड:

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       
    }

परियोजनाओं:

  • DevJet.Web (ASP.NET MVC वेब प्रोजेक्ट)

  • DevJet.Web.App.Dictionary (एक अलग कक्षा पुस्तकालय परियोजना)

    इस परियोजना में, मैंने कुछ फ़ोल्डर्स बनाए: DAL, BLL, BO, VM (व्यू मॉडल के लिए फ़ोल्डर)


नमस्ते, क्या आप साझा कर सकते हैं कि एंट्री क्लास की संरचना क्या है?
डिनिस क्रूज़

0

ऑपरेशन और प्रासंगिक डेटा के परिणाम जैसे सामान्य रूप से आवश्यक गुण वाले व्यू मॉडल बेस क्लास बनाएं, आप वर्तमान उपयोगकर्ता डेटा और भूमिकाएँ भी डाल सकते हैं

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

बेस कंट्रोलर क्लास में एक विधि है जैसे PopulateViewModelBase () यह विधि प्रासंगिक डेटा और उपयोगकर्ता भूमिकाओं को भरेगी। HasError और ErrorMessage, सेवा / db से डेटा खींचते समय अपवाद होने पर इन गुणों को सेट करें। त्रुटि दिखाने के लिए इन गुणों को बांधें। उपयोगकर्ता भूमिकाओं का उपयोग भूमिकाओं पर आधारित दृश्य को छिपाने के लिए किया जा सकता है।

व्यू मॉडल को अलग-अलग कार्यों में पॉप्युलेट करने के लिए, इसे एब्सट्रैक्ट मेथड फिलमॉडल के साथ बेस कंट्रोलर के अनुरूप बनाया जा सकता है

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

नियंत्रकों में

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;
}
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.