نویسار

یادداشت های یک برنامه نویس وب

نویسار

یادداشت های یک برنامه نویس وب

نویسار

اینجا صرفا محلی برای یاداشتها و بیان تجربه های شخصی صاحب وبلاگ می باشد.

پیوندهای روزانه

سفارشی سازی Binding یک خصوصیت از طریق Attributes

شنبه, ۲۵ آبان ۱۳۹۲، ۰۱:۵۴ ب.ظ

اگر با mvc کار کرده باشید حتما با ModelBinding آن آشنا هستید؛ DefaultModelBinder توکار آن که در اکثر مواقع، باری زیادی را از روی دوش برنامه نویسان بر می داردو کار را برای آنان راحتتر می کند، اما در بعضی مواقع این مدل بایندر پیش فرض ممکن است پاسخگوی نیاز ما در بایند کردن یک خصوصیت از یک مدل خاص نباشد، برای همین ما نیاز داریم که کمی اون رو سفارشی سازی کنیم.

برای این کار ما دو راه داریم:

1) یک مدل بایندر جدید با پیاده سازی از IModelBinder انجام دهیم. (در این حالت ما مجبوریم که مدل بایندر رو از اول جهت بایند کردن کلیه مقادیر شی مدل مون بازنویسی کنیم و در واقع امکان انتساب اون رو در سطح فقط یک خصوصیت نداریم.) (نحوه پیاده سازی قبلا در اینجا مطرح شده)

2) ModelBinder پیش فرض رو جهت پاسخگویی به نیازمون توسعه بدیم. (که در این مطلب قصد آموزشش رو داریم.)

 

فرض کنید که می خواهید از روی یک Enum در صفحه، یک DropDown معادل رو قرار بدید که به طور خودکار رشته انتخاب شده رو به یک خصوصیت مدل که از نوع بایت هست بایند بکند.

از طریق کد زیر یک DropDownFor برای Enum مورد نظر در مدل ایجاد کنیم:

@Html.DropDownListFor(model => model.AccountType, new SelectList(Enum.GetNames(typeof(Enums.AccountType))))

و کلاس Enum مورد نظر :

public enum AccountType : byte
{
   مدیر = 0,
   کاربر_حقیقی = 1,
   کاربر_حقوقی = 2,
}

حالا برای اینکه این مقدار انتخابی به صورت خودکار و از طریق امکان binding توکار خود MVC به خصوصیت AccountType مقدار دهی شود باید یک PropertyBindAttribute سفارشی بنویسیم، برای اینکار یک کلاس جدید با نام CustomBinding می سازیم و کدهای زیر رو به اون اضافه می کنیم :

namespace MvcApplication1.Models
{
    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public abstract class PropertyBindAttribute : Attribute
    {
        public abstract bool BindProperty(ControllerContext controllerContext,
        ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor);
    }

    public class ExtendedModelBinder : DefaultModelBinder
    {
        protected override void BindProperty(ControllerContext controllerContext,
            ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.Attributes.OfType<PropertyBindAttribute>().Any())
            {
                var modelBindAttr = propertyDescriptor.Attributes.OfType<PropertyBindAttribute>().FirstOrDefault();

                if (modelBindAttr.BindProperty(controllerContext, bindingContext, propertyDescriptor))
                    return;
            }

            base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
        }
    }
}

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

حالا از طریق کد های زیر، ما بایندرِ سفارشیِ خصوصیتِ خودمون رو به کلاس اضافه می کنیم :

    public class AccountTypeBindAttribute : PropertyBindAttribute
    {
        public override bool BindProperty(ControllerContext controllerContext,
            ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
        {
            if (propertyDescriptor.PropertyType == typeof(byte))
            {
                HttpRequestBase request = controllerContext.HttpContext.Request;

                byte accountType = (byte)Enum.Parse(typeof(Enums.Enums.AccountType), request.Form["AccountType"]);
                propertyDescriptor.SetValue(bindingContext.Model, accountType);

                return true;
            }
            return false;
        }
    }

در کد بالا ما مقدار رشته ای رو که از DropDown ارسال شده را به مقدار عددی متناظر تعریف شده آن در Enum تبدیل می کنیم و آن را به خصوصیت مورد نظر بازگشت می دهیم، از این به بعد فقط برای فیلدی که به شکل زیر نشانه گذاری شده باشد ما از این کلاس بایندر سفارشی استفاده می کنیم و مدل بایندر پیش فرض هم کار خود را خواهد کرد و بقیه مقادیر را بایند خواهد کرد » اطلاعات بیشتر

[AccountTypeBindAttribute]
public byte AccountType { get; set; }

حالا باید این کلاس گسترش یافته ModelBinder رو به عنوان بایندر پیش فرض mvc قرار بدهیم، برای اینکار کد زیر را به فایل Global.asax.cs اضافه کنید:

ModelBinders.Binders.DefaultBinder = new ExtendedModelBinder();

کار ما دیگه تمومه و تا اینجای کار همه چی بدرستی کار می کنه ... تا اینکه شما تصمیم میگیری که از jquery.validate.unobtrusive برای اعتبار سنجی سمت کاربر استفاده کنید و می بینید به DropDown شما هم ایراد میگیره که حتما باید از نوع عددی باشد

The field نوع کاربر : must be a number.

برای حل این مشکل هم باید به صورت دستی validation سمت کاربر رو برای این DropDown غیرفعال کرد، که باید کدهای DropDownFor که در صفحه گذاشتید رو به شکل زیر تغییر بدید:

@Html.DropDownListFor(model => model.AccountType, new SelectList(Enum.GetNames(typeof(Enums.AccountType))),new Dictionary<string, object>() {{ "data-val", "false" }})

 

 

نظرات  (۱)

با سلام
اگه مایل به تبادل لینک هستی وب ما رو با آدرس و عنوان زیر لینک کن و خبر بده تا لینکت کنیم
www.studownload.ir
دانلود سرای دانشجویی
پیشنهاد میکنم یه سر هم به وبم بزنی
تبادل لینک یا بنر باعث افزایش آمار هر دو وب خواهد شد
با تشکر

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
تجدید کد امنیتی