Generic ViewModel ها در MVC

MVC از اشیاء پیچیده (complex object) از جمله جنریک ها همچون مدل ها، پشتیبانی می کند.ما میتوانیم از این قابلیت برای پاس دادن یک نوع مدل استاندارد بین View ها و کنترلرهایمان استفاده کنیم.ما میتوانیم کد قابل استفاده مجدد را با استفاده از دستکاری این مدل های استاندارد بنویسیم.این میتواند روش استفاده ما از Mvc را تغییر دهد.در این مقاله قصد داریم که این قابلیت را معرفی کنیم.این یک الگو message-driven به MVC با استفاده از مدیریت حالت (State management) ساده را نشان می دهد.

Generic ViewModel ها در MVC

State management

مدیریت حالت (State management) یکی از مسائل زمینه های طراحی نرم افزار است.ماهیت بی قید HTTP این را برای برنامه های وب حتی پیچیده تر می کند.

در یک برنامه وب معمولی سه حالت شیء وجود دارد:

حالت شی زمانیکه خوانده میشود یا به کاربر ارسال میشود.

حالت شی زمانی که از سمت کاربر بازگشت داده میشود.

و حالت شی در پایگاه داده زمانی که شما شیء اصلاح شده را از کاربر دریافت می کنید.

اکثر برنامه های MVC حالت اصلی شی را نادیده می گیرند.که دلیل آن این است که بیشتر آن ها view model را ویرایش میکنند.

  چرخه حیات معمولی MVC به صورت زیر است:

1.خواندن پایگاه داده و دریافت مقادیر اصلی

2.ساختن ViewModel و ارسال آن به کاربر

3.کاربر ViewModel را تغییر می دهد و آن را برمیگرداند.

4.برنامه پایگاه داده را دوباره میخواند.

5.مقادیر پایگاه داده فعلی و ViewModel کاربر مقایسه می شوند تا ببینند که آیا حالت آن ها تغییر کرده است یا خیر.

6.اگر شیء پایدار تغییر کرده است، یک شیء پایدار جدید تعیین کرده و آن را ذخیره کنید.

مشکلات

یک مشکل ظریف وجود دارد.تغییراتی که کاربر ایجاد کرده است بر پایه حالت شی است که دریافت کرده است.این ممکن است با جایی که سی فقط خوانده میشود متفاوت باشد.پاسخ کاربر ممکن است متفاوت باشد اگر آنها شیء فعلی را ببینند.به روز رسانی پایگاه داده بر مبنای مقدار بازگشتی می تواند به داده بد منجر شود.

برای تصمیم گیری صحیح، بررسی تمام سه حالت مورد نیاز است.

راه حل ها

یک روش معمول برای حفظ حالت اصلی این است که قبل از ارسال آن، مدل را در سرور ذخیره کنید.این کار خواهد کرد ، اما نیاز به استراتژی برای ذخیره و واکشی و حذف داده های قدیمی تر است.روش دیگر استفاده از Symmetric Models (مدل های هم اندازه) است.Symmetric Model ها اساسا جفت مدل هایی با نوع مدل یکسان هستند.این الگو دو مدل را به view ارسال و دریافت میکند. مدل ها برگردانده شده ، مدل اصلی و مدل به روز شده است.این یک مدیریت حالت ساده است.کار کردن با جفت مدل ها ، قابلیت های دیگر مانند : ارائه مقادیر پیشفرض پویا را ارائه می دهد.

مدل های ترکیبی

در برنامه های MVC ما اغلب اشیاء داده ساده (مدل های ما) را در بین View ها و کنترلر ها مبادله میکنیم.فرم ها و تگ های ما مقادیر را از مدل نمایش میدهند ، تغییرات را میپذیرند و مدل های تغییر یافته بازگردانده شده را ارسال میکند.ما به سیستم نوع شی ای که استفاده میکنیم را با دستورالعمل model@ می گوئیم.

اشیائی که ما ارسال و دریافت میکنیم نسبتا ساده هستند ولی میتوانند همانطور که ما میخواهیم پیچیده باشند.آن ها میتوانند generics باشند.

صفحات Razor تنها اجازه میدهند که یک شی در دستور model@ باشد.از آنجا که این شیء می تواند پیچیده باشد، این یک مشکل نیست.در سیستم 

message-driven ، ما پیام ها را بین نقاط پایانی ارسال میکنیم.ما از ایده message  در مثال استفاده خواهیم کرد و یک کلاس generic message میسازیم:

Message<TIn, TOut>.

مقدار قابل قبول ما یک Person خواهد بود:

Person { string name, int age}

اکنون می توانیم پیام های person  را ایجاد، ارسال و دریافت کنیم:

  return View("ViewName",  new Message<Person, Person>()), @model Message<Person, Person>, public ViewResult PersonHandler(Message<Person, Person> message){}

بر حسب ترجیح ما ، ما میتوانیم انواع پیام ها را تولید کنیم.

 PersonMessage : Message<Person, Person>

موتور Razor  و ModelBinder در کنترل این ها مشکلی ندارند.با استفاده از Message ، ما میتوانیم دو مدل را ارسال و دریافت نماییم - یک مدل ورودی ویو (TIn) و یک مدل خروجی ویو (TOut) که در یک کانتینر (Message)  نگهداری می شود.کلاس Message  می تواند دارای خواص اضافی مانند context یا control objects باشد.

مدیریت حالت

راه های زیادی برای استفاده از مدل های ترکیبی وجود دارد.برای مدیریت ساده حالت ، TIn را با اطلاعاتی که از پایگاه داده خواندیم و TOut  را با یک Person  خالی که در قبل از آن به View ارسال کرده بودیم ،  جمع آوری می کنیم.ما تگ های خود را به TOut متصل می کنیم و مقدار اولیه را از 

TIn - <input type="text" name="@Model.TOut.Name" value="@Model.TIn.Name"> 

تنظیم میکنیم.

MVC  یک پیام با مقادیر کاربر در TOut و (با کمی کمک) مقادیر اصلی را در TIn ،  پست خواهد کرد.هنگامی که ما پیامی از اکشن کنترلرمان دریافت میکنیم ، ما میتوانیم دوباره پایگاه داده را بخوانیم و تمام سه حالت را موجود داشته باشیم. زمانیکه ما TOut را به View ارسال میکنیم نیازی نیست که یک Person خالی باشد.

ما همچنین میتوانیم چیزهایی مانند مقادیر پیش فرض را براساس حالت real-time ارسال کنیم.

کد

برای تست کردن messaging و مدل های هم نوع یک پروژه جدید MVC  ایجاد کنید.من یک پروژه MVC Core 2 / C # 7.1 را توصیه می کنم، اما در MVC Core و C # 6 نیز باید کار کنند.این احتمالا در چارچوب Net Framework کار می کند، اما ما تا به حال بررسی اش نکرده ایم.مقالات دیگر بر روی این یکی ایجاد شده و به Core نیاز دارند.

کلاس Message 

public class Message<TIn, TOut> {
    public Message() { }
    public Message(TIn inpart, TOut outpart) {
        Input = inpart;
        Output = outpart;
    }
    public TIn Input { get; set; }
    public TOut Output { get; set; }
}

کلاس Person 

فقط یکبار از این کلاس استفاده میکنیم.شما میتوانید از هر کلاسی که میخواهید استفاده کنید.

public class Person{
    public string Name{ get; set; }
    public int Age{ get; set; }
}

کنترلر

شما میتوانید از کنترلر پیشفرض Home  استفاده کنید.کدهای زیر را در بدنه آن قرار دهید.

public class HomeController : Controller
{
    public IActionResult Index() {
        var inPerson = new Person {
            Name = "Max",
            Age = 22
        };

        var outPerson = new Person();
        var message = new Message<Person, Person>(inPerson, outPerson);

        return View(message);
    }

    [Route("home-message-handler", Name = "Home.MessageHandler")]
    public IActionResult MessageHandler(Message<Person, Person> message){
        return View(message);
    }
}

View ورودی

از صفحه Index.cshtml به عنوان فرم اصلی استفاده کنید.بخش بالایی فرم ها شامل فیلدهای فرم میباشد.MVC نام صفت تگهای ورودی را متصل خواهد کرد.این به انداره کافی هوشمند است تا بفهمد که شما چه کاری را انجام میدهید، بنابراین فقط باید از خروجی استفاده کنید. [field].

این نکات به قدرت این الگو اشاره دارد ، ما میتوانیم کد قابل استفاده بنویسیم که در خروجی با استفاده از هر مدلی کار کند.اگر با Model@ شروع کنید شما میتوانید intellisense completion را دریافت کنید.بعد از آن Model@ را حذف کنید.مقدار نمایش داده شده را با استفاده از صفت مقدار تنظیم کنید.

این نیاز به استفاده از Model@ ورودی دارد.

در این مثال ساده ، حالت با استفاده از الگو ورودی مخفی مشترک مدیریت میشود.یک راه برای ایجاد این ها این است که به صورت دستی برای هر فیلد یک تگ ایجاد کنیم :

<input type="hidden" name="Input.Name" value="@Model.Input.Name" />

رویکرد دیگر این است که یک حلقه بر روی خواص شی با استفاده از کمی کد C# اعمال کنیم و تگ ها را تولید کنیم.

@using System.Reflection
@model Message<Person, Person>

<form>
    Name: <input type="text" name="Output.Name" value="@Model.Input.Name" />
    Age: <input type="text" name="Output.Age" value="@Model.Input.Age"/>
    <button asp-route="Home.MessageHandler"></button>
    
    @* State *@
    @foreach (PropertyInfo p in Model.Input.GetType().GetProperties()) {
        dynamic value = p.GetValue(@Model.Input);
        dynamic name = p.Name;
        <text> <input type="hidden" name="Input.@name" value="@value" /> </text>
    }
</form>

از طرفی ، صفحات Razor در واقع کلاس های #C با html/script در داخل آن هستند ، و صفحات Html با برخی کدهای #C در آن نیستند.

در گذشته راحت بود که به آن به عنوان یک راه دیگر نگاه کنید.Core و DI  در صفحات و TagHelper ها ماهیت #C را معلوم . در دسترس تر میکنند.

مشاهده صفحات (cshtml.) به عنوان #C و نه Html به باز کردن قدرت این فناوری ها در طراحی شما کمک می کند.

نتایج View

یک صفحه ساده MessageHandler.cshtml را برای نمایش نتایج ایجاد کنید.

@model Message<Person,Person>

Name: @Model.Input.Name -> @Model.Output.Name<br/>
Age: @Model.Input.Age -> @Model.Output.Age<br />

اجرای آن

برنامه را اجرا کنید و مقادیر text box ها را تغییر دهید و form را submit  کنید.شما میتوانید breakpoint  را در متد MessageHandler بگذارید و message را بررسی کنید تا ببیند که آن شامل حالت های اصلی و جدید است.

Wrap Up

این مقاله خلاصه ای از معرفی مفهوم generic model ها و استفاده از آن ها به عنوان یک نوع مدل مشترک است.در این مقاله نحوه ساخت یک generic model به نام Message<Input, Output> را آموزش دادیم و از آن برای حفظ حالت در یک صفحه ساده CRUD استفاده کردیم.مفهوم اصلی Core این است که با ایجاد نوع generic مانند -< Container <T1، T2، ما می توانیم از این به عنوان یک مدل استاندارد برای همه Viewهایمان استفاده کنیم.

ما میتوانیم کد قابل استفاده مجدد که بر روی container یا پارامترهای generic یا عمومی عمل میکند ، بنویسیم.این در نمونه هایی با متغییر خروجی قرار گرفته است.انجام این کار به صورت دستی آسان و قدرتمند است ، اما TagHelper ها این را به سطح دیگری می برد، همانطور که در سایر مقالات مشاهده خواهیم کرد.

زمانیکه ما از یک نوع شی برای T1 و T2 استفاده میکنیم ، ما میتوانیم چیزهایی مثل مدیریت حالت یا مقادیر پیشفرض پویا را پیاده سازی کنیم.ما همچنین می توانیم از الگو مدل های نامتقارن استفاده کنیم که در آن مدل ها متفاوت هستند.یک استفاده آن بازگرداندن زیرمجموعه ای از مدل ورودی در مدل خروجی است.این می تواند یک جایگزین برای استفاده (Bind) در اکشن هایمان باشد.ما همچنین می توانیم انواع مدل های متفاوتی را استفاده کنیم و  View های ما را تبدیل کنیم.در همه این موارد، ما می توانیم اشیای پیش و پست را با هم بسته بندی کنیم.که این مباحث در سایر مقالات مورد بررسی قرار میگیرند.با عبور از اشیاء Message به جای اشیاء داده ، ما میتوانیم کدهای کنترل کردن message و کد های مسیریابی مبتنی بر محتوا را بنویسیم.این به ما اجازه میدهد که از Mvc علاوه بر حالت مشترک CRUD در رویکردهای message-driven یا workflow استفاده کنیم.این سناریوها در مقالات بعدی نیز مورد بررسی قرار می گیرند.

آموزش سی شارپ