ايجاد Model Binder سفارشی در ASP.NET MVC
شناسه پست: 1014
بازدید: 1286

در ASP.NET MVC، ما مدلهایمان را با استفاده از Entity Framework، ADO.NET یا هر تکنیک دیگری از دسترسی به داده ها میتوانیم بیسازیم. سپس در اکشن هایی که در کنترلهایمان داریم میتوانیم از این مدل ها به عنوان ورودی هایمان استفاده نماییم. اگر ما از EntityFramework استفاده کنیم سپس Application، آبجکتهایی را که ما می توانیم به عنوان موجودیت در برنامه استفاده نماییم را در اختیارمان قرار می دهد. سپس از این آبجکتها در اکشن متدها در MVC می توانیم به عنوان ورودی استفاده نماییم. اینجا Model binder مسئول map کردن المنتهای درون view به خصوصیات مدلها می باشد.

قسمت اول: Model Binder درASP.NET MVC

MVC از انواع زیر برای bind کردن مدل استفاده می نماید:

IModelBinder interface – این اینترفیس متدهایی که برای یک Model Binder مورد نیاز می باشد را تعریف می کند. مثل متد BindModel . این متد مسئول Bind کردن یک مدل به تعدای value با استفاده از ControllerContext  و BindingContext می باشد.

IModelBinderProvider interface – این اینترفیس شامل متدهایی برای پیاده سازی داینامیک model binding برای کلاسهایی که اینترفیس IModelBinder را پیاده سازی می کنند می باشد. این اینترفیس برای مدیریت Binder سفارشی از داده های ارسال شده توسط کاربر در View است.

کلاس DefaultModelBinder 

  • این کلاس برای map کردن درخواست مرورگر به آبجکتی از داده ها می باشد. این کلاس یک درخواستی مشخص از IModelBinder است.
  • این کلاس به صورت پیش فرض برای map کردن داده ها ارسال شده توسط المنت های موجود در view به خصوصیات درون مدل که توسط کنترلر می تواند در پردازش های بعدی استفاده شود به کار گرفته می شود.

نمودار Model Binder

Model Binder سفارشی

قسمت دوم: پیاده سازی Model Binder های سفارشی

ما قصد داریم این مفهوم را طی یک مثال به شما نشان دهیم.

مرحله 1: یک پروژه از نوع  ASP.NET Web application با نام MVC_CustomModelBinder در ویژوال استودیو ایجاد نمایید:

Custom Model Binder
Custom Model Binder

سپس در پنجره بعدی نوع پروژه را Empty انتخاب نمایید:

mvc-empty-template

مرحله 2: یک دیتابیس جدید از Sql Server با نام ApplicationDB.mdf اضافه نمایید. در فولدر models، یک ADO.NET Entity Data Model جدید با نام ApplicationEntities بسازید در ویزارد، Code-First from database را مانند شکل زیر انتخاب نمایید:

ef-database-first

ApplicationDB.mdf را انتخاب نمایید و finish را بزنید. این کلاس ApplicationEntities را درون فایل کلاس  ApplicationEntities.cs ایجاد می نماید. این کلاس از DbContext مشتق شده است. ما از این کلاس برای عملیات دیتاببیس استفاده می کنیم.

مرحله 3: در فولدر models، یک کلاس جدید با نام Employee.cs ایجاد نمایید و خصوصیات زیر را درون آن قرار دهید:

using System.ComponentModel.DataAnnotations;
 
namespace MVC_CustomModelBinder.Models
{
    public class Employee
    {
        [Key]
        public int EmpNo { get; set; }
        public string EmpName { get; set; }
        public decimal Salary { get; set; }
    }
}

 

این کلاس یک موجودیت است که فیلد EmpNo در آن به عنوان کلید اولیه می باشد.

از آنجایی که ما قصد داریم این کلاس را به عنوان یک جدول درون دیتابیس داشته باشیم کد زیر را در کلاس ApplicationEntities قرار دهید:

مرحله 4: یک کنترلر با نام Employee  در فولدر Controllers ایجاد نمایید و کدهای زیر را درون آن قرار دهید:

using System.Linq;
using System.Web.Mvc;
using MVC_CustomModelBinder.Models;
using System.Xml.Serialization;
 
namespace MVC_CustomModelBinder.Controllers
{
    public class EmployeeController : Controller
    {
        ApplicationEntities ctx;
        public EmployeeController()
        {
            ctx = new ApplicationEntities();
        }
 
        // GET: Employee
        public ActionResult Index()
        {
            var Emps = ctx.Employees.ToList();
            return View(Emps);
        }
        public ActionResult Create()
        {
            var empPostedData = new XmlSerializer(typeof(Employee));
            var Emp = (Employee)empPostedData.Deserialize(HttpContext.Request.InputStream);
            ctx.Employees.Add(Emp);
              
            return View("Index") ;
        }
    }
}

کلاس بالا از ApplicationEntities  برای برقراری ارتباط با دیتابیس استفاده میکند. اکشن Create اینجا کار اصلی را برای ما انجام میدهد. اینجا از کلاس XmlSerializer برای تعریف نوع داده های XML که توسط request که از طرف کاربر post می شود استفاده می شود. سپس با استفاده از متد Deserialize، request مورد نظر خوانده شده و در آبجکت Employee  ذخیره می گردد که در خط بعدی برای عملیات دیتابیس استفاده می شود. جهت تست داخل متد یک Break Point قرار می دهیم. جهت تست داده ها ما از ابزار fiddler استفاده می نماییم.(از ایزار postman نیز جهت تست می توانید استفاده نمایید)

مرحله 5: در نتیجه fiddler را باز نموده و از url زیر در composer  با داده های xml نشان داده شده در تصویر زیر متد create را اجرا نمایید:

ابزار fiddler

حال پروژه را اجرا نمایید.

دکمه Execute را کلیک نمایید نتیجه Debug به صورت شکل زیر نمایش داده می شود:

posted-data در fiddler

همونطور که میبینید داده های مورد نظر ارسال شده اند و ما از آن می توانیم در عمیلات خود استفاده نماییم. اما از آنجایی که بهتر است ما در هر اکشن متد با استفاده از داده های ارسال شده که باید به صورت خودکار با آبجکت مورد نظر map شده باشد یک مکانیسم داشته باشیم اینجا جایی است که ModelBinder  وارد عمل می شود. در این موردی که ما مثال زدیم نیاز است که یک زیرساخت را با استفاده از داده های xml ارسال شده که به آبجکت Employee  بایند می شود پیاده سازی نماییم. ما می توانیم این کار را با Custom Model Binder انجام دهیم.

مرحله 6: در پروژه یک فولدر با نام CustomModelBinders ایجاد نمایید. در این فولدر یک کلاس با کدهای زیر ایجاد نمایید:

using System;
using System.Web;
 
using System.Web.Mvc;
using System.Xml.Serialization;
 
namespace MVC_CustomModelBinder.CustomModelBinders
{
    public class XMLToObjectModelBinder : IModelBinder
    {
        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            try
            {
                //1.
                var model = bindingContext.ModelType;
                //2.
                var data = new XmlSerializer(model);
                //3.
                var receivedStream = controllerContext.HttpContext.Request.InputStream;
                //4.
                return data.Deserialize(receivedStream);
            }
            catch (Exception ex)
            {
                bindingContext.ModelState.AddModelError("Error", "Received Model cannot be serialized");
                return null;
            }
 
        }
    }
    public class XMLToObjectModelBinderProvider : IModelBinderProvider
    {
        public IModelBinder GetBinder(Type modelType)
        {
            //5.
            var receivedContentType = HttpContext.Current.Request.ContentType.ToLower();
            if (receivedContentType != "text/xml")
            {
                return null;
            }
 
            return new XMLToObjectModelBinder();
        }
    }
 
}

 

XMLToObjectModelBinder  شامل مشخصات زیر می باشد:

  1. اول از همه این کد، نوع مدل را تحت مدل Context مورد نظر می خواند.
  2. نمونه ای از XmlSerializer جهت کار با داده های ارسال شده تعریف شده است.
  3. داده های دریافت شده از request ارسال شده خوانده می شود.
  4. داده های دریافت شده deserialize می شود.

کلاس XMLToObjectModelBindingProvider  رابط IModelBinderProvider  و متد GetBinder را پیاده سازی می نماید. این متد نوع محتوا را از request ورودی می خواند. اگر نوع محتوای دریافت شده از نوع text/xml نباشد سپس null برگردانده می شود در غیر اینصورت XMLToObjectModelBinder  برگردانده خواهد شد.

مرحله 7: در نتیجه کلاس model provider بالا را در application اضافه می نماییم که در نتیجه application آن را در فرایند Bind کردن مدل بارگزاری خواهد کرد. برای این کار فایل Global.asax را باز نموده و خط مشخص شده را در متد Application_Start() به صورت زیر د رآن اضافه نمایید:

protected void Application_Start()
{
    ModelBinderProviders.BinderProviders.Insert(0, new XMLToObjectModelBinderProvider());
    AreaRegistration.RegisterAllAreas();
    RouteConfig.RegisterRoutes(RouteTable.Routes);
}

 

ModelBinderProviders بایندرهای سفارشی که در مرحله قبل ساختیم را در application  اضافه خواهد کرد.

مرحله 8: کد اکشن متد Create  در EmployeeController  را به صورت زیر تغییر دهید:

public ActionResult Create(Employee Emp)
{
    ctx.Employees.Add(Emp);
    ctx.SaveChanges();
    return View("Index") ;
}

حال یک breakpoint درون کد قرار می دهیم و پروژه را با ابزار Fiddler اجرا می کنیم. همان داده هایی که در مرحله 5 وارد نموده بودیم را مجددا اینجا وارد می نماییم. داده های ارسال شده در شکل زیر نمایش داده شده اند:

اکشن متد کارمند در mvc

این تصویر نشان می دهد که داده ها به داخل آبجکت Employee با موفقیت deserialize  شده است.

همانطور که مشاهده نمودید ما در این مثال ModelBinder سفارشی خودمان را پیاده سازی نمودیم و قبل از عملیات ثبت در دیتابیس در ModelBinder سفارشی خود تعیین نمودیم که اگر نوع داده های ارسال شده به غیر از text/xml بود null برگدانده شود و عملیات Bind به آبجکت Employee  صورت نگیرد.

ایجاد ModelBinder سفارشی برای فیلد تاریخ

به عنوان مثالی دیگر ما قصد داریم 3 فیلد درون فرم برای گرفتن روز، ماه و سال در نظر بگیریم. قصد داریم زمانی که کاربر داده ها را وارد نمود و ارسال کرد داده های وارد شده در این 3 فیلد با یکدیگر الحاق شده و در قالب یک فیلد تاریخ برگردانده شود.

برای این کار View زیر را در نظر بگیرید:

در فولدر Models، یک کلاس با نام HomePageModels ایجاد نموده و فیلدهای زیر را درون آن قرار می دهیم:

سپس یک کلاس ModelBinder سفارشی با نام HomeCustomBinder در فولدر CustomModelBinders ایجاد می نماییم و کدهای زیر را درون آن قرار می دهیم:

همانطور که در کد بالا مشاهده می نمایید ما request مورد نظر را دریافت نموده و مقدارهای وارد شده در فیلدهای ورودی را استخراج می نماییم. سپس ما می توانیم این فیلدها را به هر صورتی که تمایل داشتیم دستکاری نماییم. همانطور که مشاهده می نمایید من آنها را درون یک property به نام Date قرار داده ام.

توجه: ما اگر تمایل نداریم که برای هر مدل و فیلد در application کلاس binding  سفارشی پیاده سازی نماییم در این صورت می توانیم از کلاس DefaultModelBinder  یک کلاس مشتق شده بسازیم و متد BindModel  درون آن را همانند کدهای زیر override نماییم:

حال که ما ModelBinder سفارشی خودمان را ایجاد نمودیم باید آن را توسط کد زیر در فایل Global.asax در متد Application_Start اضافه نماییم:

در آخر نیاز است که ما اکشن متد مورد نظر را محدود به استفاده از این ModelBinder نماییم که برای این منظور از این Attribute استفاده می نماییم:

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

نتیجه گیری

در نتیجه شما می توانید با استفاده از ModelBinder ، مکانیسمی را برای خود طراحی کنید که قبل از map شدن request ارسالی به Data Model، ان را به سلیقه خود دستکاری و یا مدیریت نمایید.

نویسنده

امید عباسی
من امید عباسی هستم. سالهاست که در زمینه برنامه نویسی با تکنولوژی دات نت فعالیت میکنم و عاشق این هستم که تجربیات و دانش خودم را در این زمینه با دیگران به اشتراک بزارم. خیلی دوست دارم که نظر و انتقاد خودتون رو در مورد این نوشته برای من بنویسید تا بتونم در آینده، مطالب بهتر و ارزشمندتری را برای شما فراهم کنم. در صورت داشتن هرگونه سوال هم در قسمت دیدگاه ها میتونید با بنده در ارتباط باشید