- در استاندارد 9147 ، جای ژ و پ مطابق صفحه کلیدی که در بازار ایران فروخته میشود نیست (و باید به همه پاسخگو باشید که چرا اینطوری است).
- ی و ک در آن اصلاح شده و دیگر عربی نیست که چقدر هم خوب، اما من تعداد زیادی برنامه و همچنین تعداد قابل توجهی فایل word دارم که مطابق استاندارد 2091 تهیه شدهاند. مشکلات ی و ک فارسی و عربی را هم در اینجا ذکر کردهام و به دنبال باگهای جدید در برنامهها نمیگردم.
- با ی و ک فارسی اگر در گوگل جستجو کنید تعداد پاسخهای مرتبط یافت شده بسیار بسیار کمتر از حالتی خواهد بود که با ی و ک عربی جستجو کنید.
- در یک سازمان باید رویهای واحد در این مورد برقرار باشد. یا همه باید از 2091 استفاده کنند و یا همه از 9147.
نسخهی کامل استاندارد 9147 را از سایت آقای اخگری میتوانید دریافت نمائید:
به همین دلایل خصوصا قسمت دوم (هر چند ممکن است با آن موافق نباشید)، نیاز به صفحه کلید مطابق استاندارد 2091 نسخهی 64 بیتی داشتم.
برای تهیه صفحه کلید فارسی از برنامه Microsoft Keyboard Layout Creator استفاده میشود که نسخهی 1.4 آنرا از آدرس زیر میتوانید دریافت نمائید. این نسخه قابلیت تولید فایلهای dll مرتبط 64 بیتی را هم دارد:
یکی از قابلیتهای این برنامه بارگذاری صفحه کلید فارسی جاری سیستم است:
صفحه کلید استاندارد 2091 پروژه وب فارسی را اگر با آن باز کرده و سپس از منوی Project گزینهی Build DLL and setup package را انتخاب نمائید، با خطای زیر متوقف خواهید شد:
ERROR: 'VK_SPACE' in Shift State 'Shift' must be made up of white space character(s), but is defined as '' (U+200c) instead.
39 SPACE 0 0020 200c 0020 -1 // SPACE, ZERO WIDTH NON-JOINER, SPACE, <none>
0 //Column 4 1 //Column 5 : Shft 2 //Column 6 : Ctrl 3 //Column 7 : Shft Ctrl
اما این برنامهی محترم دقیقا همین مورد را غلط گرفته و فایل dll نهایی را تولید نمیکند (مطابق خطایی که ذکر شد).
برنامهی Microsoft Keyboard Layout Creator هم با دات نت فریم ورک نوشته شده است. اگر کمی با reflector به آنالیز آن بپردازیم به کلاس Accept و متد زیر خواهیم رسید:
private bool VerifySpaceBarIntegrity(ShiftState ss, bool fMustBeDefined)
if (!Utilities.IsSpacing(ss.Characters)) { this.WriteErrorToLogFile("SpaceKeyNeedsWhiteSpaceCharacters", new string[] { "VK_" + Utilities.VkStringOfIvk(ss.VK), this.m_stStateLabel[ss.State], ss.Characters, Utilities.FromCharacterToUPlusForm(ss.Characters) }); flag = false; /*اینجا باید اصلاح شود!*/ }
با استفاده از این نسخه (ابتدا برنامه اصلی را نصب کرده و سپس فایل exe را جایگزین نمائید)، به راحتی میتوان نسخهی استاندارد و اصلی 2091 را بارگذاری و مجددا کامپایل کرد (بدون توقف کامپایل به خاطر فقط یک نیم فاصلهی غیرمعتبر از دیدگاه نویسندگان اصلی برنامه). به این صورت فایلهای 64 بیتی لازم هم تولید میشوند ( ممکن است در حین کار با برنامهی patch شده، نمایش خطای کار با نیم فاصله را مجددا دریافت کنید؛ اما مهم نیست . نمونهی وصله شده، بدون مشکل حاصل را کامپایل میکند که در نمونهی اصلی اینطور نیست. یعنی پس از overwrite کردن فایل exe موجود، با نمونه patch شده، برنامه کار کامپایل را کامل میکند) که حاصل نهایی را از آدرس زیر میتوانید دریافت نمائید:
این فایلها دقیقا بر مبنای همان فایلهای پروژه وب فارسی استاندارد 2091 هستند؛ با این تفاوت که نسخههای غیر 32 بیتی هم در آن لحاظ شدهاند.
نصب این dllها هم از ویندوز ویستا به بعد داستان خودش را پیدا کرده؛ ابتدا باید take ownership شود، سپس میتوان فایل اصلی را به سادگی جایگزین کرد و سپس reboot .
برای این منظور ابتدا به پوشهی system32 ویندوز مراجعه کرده و فایل KBDFA.DLL را پیدا کنید.
در ادامه به پنجره خواص آن (کلیک راست و انتخاب properties) مراجعه و برگهی Security آنرا انتخاب کنید.
در این قسمت بر روی دکمهی Advanced کلیک نمائید.
در صفحهی باز شده به برگهی Owner مراجعه کنید.
در این قسمت بر روی دکمهی Edit کلیک نموده و کاربر خودتان را اضافه نمائید.
پس از طی این مرحله (یا همان take ownership) به برگه security مراجعه کرده و به کاربر خودتان دسترسی full control بدهید.
اکنون مجوز تغییر این فایل را بدون هیچ مشکلی خواهید یافت.
در پایان reboot را فراموش نکنید.
راه دوم:
یا برنامه نصاب حاصل از برنامهی Microsoft Keyboard Layout Creator، مدخل جدیدی را به صفحه کلیدهای قابل انتخاب ویندوز در کنترل پنل اضافه میکند که نیازی به reboot ندارد.
کلاس زیر را در نظر بگیرید:
public class Data { public string Name { get; set; } public string ValueInHex { get; set; } }
در اینجا مقدار Hex برایمان قابل فهم نیست. سناریویی را در نظر بگیرید که مقادیر باید داخل دیتابیس به صورت Hex نگهداری شوند، اما میخواهیم هنگام دیباگ، مقدار پراپرتی HexValue به صورت قابل درک و decimal آن نمایش داده شود.
برای انجام اینکار میتوانیم از DebuggerTypeProxy استفاده کنیم. ابتدا کلاسی ایجاد میکنیم که بعنوان proxy، مقادیر را به شکلی که نیاز داریم نمایش دهد. این کلاس object اصلی را در Constructor دریافت کرده و مقادیر مورد نظرمان، از طریق property هایی که در آن تعریف میکنیم قابل دسترسی هستند:
public class DataDebugView { private readonly Data _data; public DataDebugView(Data data) { _data = data; } public string DecimalValue { get { bool isValidHex = int.TryParse(_data.HexValue, System.Globalization.NumberStyles.HexNumber, null, out var value); return isValidHex ? value.ToString() : "INVALID HEX STRING"; } } }
در نهایت برای اعمال کردن این کلاس proxy، از ویژگی DebuggerTypeProxy بر روی کلاس اصلی استفاده میکنیم:
[DebuggerTypeProxy(typeof(DataDebugView))] public class Data { public string Name { get; set; } public string HexValue { get; set; } }
بعد از اعمال تغییرات و اجرای دوباره برنامه، نحوه نمایش مقادیر کلاس به این صورت تغییر خواهند یافت:
آمار فریم ورکهای استفاده شده در برنامه نویسی وب
اکثر اوقات از این آمارها برای اثبات برتری فریم ورکها نسبت به هم استفاده میکنند ولی این آمارها به خودی خود ثابت کننده مطلب مهمی (مخصوصا برتری فریم ورک خاصی) نیست به چند دلیل:
- مشخص نیست این سایتها که از فناوری خاصی استفاده میکنند، مهم هستند یا نه. هرکسی از راه رسیده یک سایت وارز درست کرده با وردپرس. مهمه؟ در آمارها باید به حساب بیاد؟
- مشخص نیست توزیع سیستم عاملی آنها به چه صورتی است. ممکن است یک مورد توزیع سیستم عاملی آنچنانی نداشته باشد اما بر روی مثلا ویندوز حرف اول را بزند.
- مشخص نیست قدمت فریم ورک مورد استفاده تا چه اندازه تاثیر گذار بوده. اگر فریم ورکی 18 سال است که وجود دارد، تا این حد که هنوز پشتیبانی کاملی از یونیکد ندارد (مثل php) آیا بهتر است از فریم ورک دیگری که قدمت کمتری دارد اما بسیار کاملتر است و کمی به زمان نیاز دارد تا ارزشش مشخص شود؟
ModelBinder سفارشی در ASP.NET MVC
میخواستم بپرسم override کردن BindModel یا BindProperty برای زمانییه که ما به تمام دیتا هامون دسترسی داریم حالا شکل برگرداندنمون فرق میکنه؟
اگر اینطوره سوالم اینه که برای حالتی که مدل ما به شکل زیر هست چگونه Items را Bind کنم چون از هر روشی میرم null هست!
public class Model { public Model() { Items = new List<ItemModel>(); } public Guid Id { get; set; } public Guid ProductId { get; set; } public List<ItemModel> Items { get; set; } } public class ItemModel { Public Guid Id public string Title{ get; set; } public int Value { get; set; } }
@model List<Model>
گرفتن خروجی CamelCase از JSON.NET
یک سری از کتابخانههای جاوا اسکریپتی سمت کلاینت، به نامهای خواص CamelCase نیاز دارند و حالت پیش فرض اصول نامگذاری خواص در دات نت عکس آن است. برای مثال بجای UserName به userName نیاز دارند تا بتوانند صحیح کار کنند.
روش اول حل این مشکل، استفاده از ویژگی JsonProperty بر روی تک تک خواص و مشخص کردن نامهای مورد نیاز کتابخانهی جاوا اسکریپتی به صورت صریح است.
روش دوم، استفاده از تنظیمات ContractResolver میباشد که با تنظیم آن به CamelCasePropertyNamesContractResolver به صورت خودکار به تمامی خواص به صورت یکسانی اعمال میگردد:
var json = JsonConvert.SerializeObject(obj, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
درج نامهای المانهای یک Enum در خروجی JSON
اگر یکی از عناصر در حال تبدیل به JSON، از نوع enum باشد، به صورت پیش فرض مقدار عددی آن در JSON نهایی درج میگردد:
using Newtonsoft.Json; namespace JsonNetTests { public enum Color { Red, Green, Blue, White } public class Item { public string Name { set; get; } public Color Color { set; get; } } public class EnumTests { public string GetJson() { var item = new Item { Name = "Item 1", Color = Color.Blue }; return JsonConvert.SerializeObject(item, Formatting.Indented); } } }
{ "Name": "Item 1", "Color": 2 }
الف) مزین کردن خاصیت از نوع enum به ویژگی JsonConverter از نوع StringEnumConverter:
[JsonConverter(typeof(StringEnumConverter))] public Color Color { set; get; }
return JsonConvert.SerializeObject(item, new JsonSerializerSettings { Formatting = Formatting.Indented, Converters = { new StringEnumConverter() } });
تهیه خروجی JSON از مدلهای مرتبط، بدون Stack overflow
دو کلاس گروههای محصولات و محصولات ذیل را درنظر بگیرید:
public class Category { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Product> Products { get; set; } public Category() { Products = new List<Product>(); } } public class Product { public int Id { get; set; } public string Name { get; set; } public virtual Category Category { get; set; } }
با توجه به این دو کلاس، سعی کنید مثال ذیل را اجرا کرده و از آن، خروجی JSON تهیه کنید:
using System.Collections.Generic; using Newtonsoft.Json; using Newtonsoft.Json.Converters; namespace JsonNetTests { public class SelfReferencingLoops { public string GetJson() { var category = new Category { Id = 1, Name = "Category 1" }; var product = new Product { Id = 1, Name = "Product 1" }; category.Products.Add(product); product.Category = category; return JsonConvert.SerializeObject(category, new JsonSerializerSettings { Formatting = Formatting.Indented, Converters = { new StringEnumConverter() } }); } } }
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll Additional information: Self referencing loop detected for property 'Category' with type 'JsonNetTests.Category'. Path 'Products[0]'.
راه حل اول:
به تنظیمات JSON.NET، مقدار ReferenceLoopHandling = ReferenceLoopHandling.Ignore را اضافه کنید تا از حلقهی بازگشتی بیپایان جلوگیری شود:
return JsonConvert.SerializeObject(category, new JsonSerializerSettings { Formatting = Formatting.Indented, ReferenceLoopHandling = ReferenceLoopHandling.Ignore, Converters = { new StringEnumConverter() } });
به تنظیمات JSON.NET، مقدار PreserveReferencesHandling = PreserveReferencesHandling.Objects را اضافه کنید تا مدیریت ارجاعات اشیاء توسط خود JSON.NET انجام شود:
return JsonConvert.SerializeObject(category, new JsonSerializerSettings { Formatting = Formatting.Indented, PreserveReferencesHandling = PreserveReferencesHandling.Objects, Converters = { new StringEnumConverter() } });
{ "$id": "1", "Id": 1, "Name": "Category 1", "Products": [ { "$id": "2", "Id": 1, "Name": "Product 1", "Category": { "$ref": "1" } } ] }
وضعیت خروجی متدهای بازنویسی شده تا پیش از C# 9.0
برای توضیح بهتر Covariant returns، نیاز است مثال زیر را بررسی کنیم:
public abstract class Product { public string Name { get; set; } public abstract ProductOrder Order(int quantity); } public class Book : Product { public string ISBN { get; set; } public override ProductOrder Order(int quantity) => new BookOrder { Quantity = quantity, Product = this }; } public class ProductOrder { public int Quantity { get; set; } } public class BookOrder : ProductOrder { public Book Product { get; set; } }
همانطور که مشاهده میکنید، در کلاس Book، تنها خروجی که برای متد Order بازنویسی شده میتوان درنظر گرفت، همانی است که در کلاس پایهی Product تعریف شدهاست و قابل تغییر نیست؛ یعنی همان ProductOrder.
همچنین در حین استفادهی از این کلاسها، تبدیل خروجی متد Order، به BookOrder ضروری است:
var book = new Book { Name = "My book", ISBN = "11-1-12-22-0" }; BookOrder orderBook = (BookOrder)book.Order(1);
امکان تغییر خروجی متدهای بازنویسی شده در C# 9.0
در C# 9.0 با مجاز اعلام شدن خروجیهای covariant، میتوان تغییرات زیر را به کدهای فوق اعمال کرد:
public class Book : Product { public string ISBN { get; set; } public override BookOrder Order(int quantity) => new BookOrder { Quantity = quantity, Product = this }; }
مزایای این ویژگی:
- داشتن یک خروجی مختص و متناسب با کلاس کتاب، مانند BookOrder؛ بجای ارائهی یک خروجی بسیار عمومی ProductOrder.
- نیاز به کار با Generics را برای اینگونه اختصاصی سازیها منتفی میکند.
- با این تغییر، دیگر نیازی به تبدیل نوع خروجی متد Order یک کتاب نیست و سطر سفارش دهی را میتوان به صورت زیر خلاصه کرد:
BookOrder orderBook = book.Order(1);
مدل برنامه
using System.ComponentModel; using System.ComponentModel.DataAnnotations; namespace Mvc4TwitterBootStrapTest.Models { public class User { [DisplayName("نام")] [Required(ErrorMessage="لطفا نام را تکمیل کنید")] public string Name { set; get; } [DisplayName("نام خانوادگی")] [Required(ErrorMessage = "لطفا نام خانوادگی را تکمیل کنید")] public string LastName { set; get; } } }
کنترلر برنامه
using System.Web.Mvc; using Mvc4TwitterBootStrapTest.Models; namespace Mvc4TwitterBootStrapTest.Controllers { public class HomeController : Controller { [HttpGet] public ActionResult Index() { return View(); } [HttpPost] public ActionResult Index(User user) { if (this.ModelState.IsValid) { if (user.Name != "Vahid") { this.ModelState.AddModelError("", "لطفا مشکلات را برطرف کنید!"); this.ModelState.AddModelError("Name", "نام فقط باید وحید باشد!"); return View(user); } // todo: save ... return RedirectToAction("Index"); } return View(user); } } }
طراحی View سازگار با Twitter bootstrap
@model Mvc4TwitterBootStrapTest.Models.User @{ ViewBag.Title = "تعریف کاربر"; } @using (Html.BeginForm("Index", "Home", FormMethod.Post, new { @class = "form-horizontal" })) { @Html.ValidationSummary(true, null, new { @class = "alert alert-error alert-block" }) <fieldset> <legend>تعریف کاربر</legend> <div class="control-group"> @Html.LabelFor(x => x.Name, new { @class = "control-label" }) <div class="controls"> @Html.TextBoxFor(x => x.Name) @Html.ValidationMessageFor(x => x.Name, null, new { @class = "help-inline" }) </div> </div> <div class="control-group"> @Html.LabelFor(x => x.LastName, new { @class = "control-label" }) <div class="controls"> @Html.TextBoxFor(x => x.LastName) @Html.ValidationMessageFor(x => x.LastName, null, new { @class = "help-inline" }) </div> </div> <div class="form-actions"> <button type="submit" class="btn btn-primary"> ارسال</button> </div> </fieldset> }
1) کلاس form-horizontal به فرم جاری اضافه شده است تا در ادامه بتوانیم برچسبها را در کنار تکست باکسها به صورت افقی نمایش دهیم.
2) به ValidationSummary کلاسهای alert alert-error alert-block انتساب داده شدهاند تا نمایش خطای کلی یک فرم، متناسب با Twitter bootstrap شود.
3) هر خاصیت، با یک div دارای کلاس control-group محصور شده است.
4) هر برچسب دارای کلاس control-label است.
5) به هر ValidationMessageFor کلاس help-inline انتساب داده شده است.
6) کنترلهای ورودی برنامه در divایی با کلاس controls محصور شدهاند.
7) قسمت دکمه فرم، در div ایی با کلاس form-actions قرار گرفته تا یک زمینه خاکستری در اینجا ظاهر شود.
8) دکمه فرم، با کلاس btn خاص bootstrap تزئین شده.
در این حالت به شکل فوق خواهیم رسید. همانطور که ملاحظه میکنید در صورتیکه بر روی دکمه ارسال کلیک شود، همان رنگهای متداول jQuery Validator ظاهر میشوند و کل ردیف همانند روشهای متداول Twitter bootstrap دارای رنگ قرمز انتساب یافته توسط کلاس error نخواهد شد.
برای رفع این مشکل باید اندکی اسکریپت نویسی کرد:
@section javaScript { <script type="text/javascript"> $.validator.setDefaults({ highlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).addClass(errorClass).removeClass(validClass); } else { $(element).addClass(errorClass).removeClass(validClass); $(element).closest('.control-group').removeClass('success').addClass('error'); } $(element).trigger('highlated'); }, unhighlight: function (element, errorClass, validClass) { if (element.type === 'radio') { this.findByName(element.name).removeClass(errorClass).addClass(validClass); } else { $(element).removeClass(errorClass).addClass(validClass); $(element).closest('.control-group').removeClass('error').addClass('success'); } $(element).trigger('unhighlated'); } }); $(function () { $('form').each(function () { $(this).find('div.control-group').each(function () { if ($(this).find('span.field-validation-error').length > 0) { $(this).addClass('error'); } }); }); }); </script> }
اینبار در حالت اعتبار سنجی، به شکل ذیل خواهیم رسید:
و یا اگر کاربر فیلد را تکمیل کند، رنگ فیلد و ردیف تعیین اعتبار شده، سبز میشود:
و در حالت خطاهای سفارشی سمت سرور، پس از postback، شکل زیر نمایش داده میشود:
public class UserModel { public int Id { get; set; } [Required(ErrorMessage = "(*)")] [Display(Name = "نام")] [StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "نام باید حداقل 3 و حداکثر 10 حرف باشد")] public string FirstName { get; set; } [Required(ErrorMessage = "(*)")] [Display(Name = "نام خانوادگی")] [StringLength(maximumLength: 10, MinimumLength = 3, ErrorMessage = "نام خانوادگی باید حداقل 3 و حداکثر 10 حرف باشد")] public string LastName { get; set; } }
public class UserViewModel { public string FirstName { get; set; } public string LastName { get; set; } }
در ادامه قصد داریم راه حلی را به کمک جایگزین سازی Providerهای توکار ASP.NET MVC با نمونهی سازگار با AutoMapper، ارائه دهیم، به نحوی که دیگر نیازی نباشد تا این ویژگیها را در ViewModelها تکرار کرد.
قسمتهایی از ASP.NET MVC که باید جهت انتقال خودکار ویژگیها تعویض شوند
ASP.NET MVC به صورت توکار دارای یک ModelMetadataProviders.Current است که از آن جهت دریافت ویژگیهای هر خاصیت استفاده میکند. میتوان این تامین کنندهی ویژگیها را به نحو ذیل سفارشی سازی نمود.
در اینجا IConfigurationProvider همان Mapper.Engine.ConfigurationProvider مربوط به AutoMapper است. از آن جهت استخراج اطلاعات نگاشتهای AutoMapper استفاده میکنیم. برای مثال کدام خاصیت Model به کدام خاصیت ViewModel نگاشت شدهاست. اینکارها توسط متد الحاقی GetMappedAttributes انجام میشوند که در ادامهی مطلب معرفی خواهد شد.
public class MappedMetadataProvider : DataAnnotationsModelMetadataProvider { private readonly IConfigurationProvider _mapper; public MappedMetadataProvider(IConfigurationProvider mapper) { _mapper = mapper; } protected override ModelMetadata CreateMetadata( IEnumerable<Attribute> attributes, Type containerType, Func<object> modelAccessor, Type modelType, string propertyName) { var mappedAttributes = containerType == null ? attributes : _mapper.GetMappedAttributes(containerType, propertyName, attributes.ToList()); return base.CreateMetadata(mappedAttributes, containerType, modelAccessor, modelType, propertyName); } }
شبیه به همین کار را باید برای ModelValidatorProviders.Providers نیز انجام داد. در اینجا یکی از تامین کنندههای ModelValidator، از نوع DataAnnotationsModelValidatorProvider است که حتما نیاز است این مورد را نیز به نحو ذیل سفارشی سازی نمود. در غیراینصورت error messages موجود در ویژگیهای تعریف شده، به صورت خودکار منتقل نخواهند شد.
public class MappedValidatorProvider : DataAnnotationsModelValidatorProvider { private readonly IConfigurationProvider _mapper; public MappedValidatorProvider(IConfigurationProvider mapper) { _mapper = mapper; } protected override IEnumerable<ModelValidator> GetValidators( ModelMetadata metadata, ControllerContext context, IEnumerable<Attribute> attributes) { var mappedAttributes = metadata.ContainerType == null ? attributes : _mapper.GetMappedAttributes(metadata.ContainerType, metadata.PropertyName, attributes.ToList()); return base.GetValidators(metadata, context, mappedAttributes); } }
و در اینجا پیاده سازی متد GetMappedAttributes را ملاحظه میکنید.
ASP.NET MVC هر زمانیکه قرار است توسط متدهای توکار خود مانند Html.TextBoxFor, Html.ValidationMessageFor، اطلاعات خاصیتها را تبدیل به المانهای HTML کند، از تامین کنندههای فوق جهت دریافت اطلاعات ویژگیهای مرتبط با هر خاصیت استفاده میکند. در اینجا فرصت داریم تا ویژگیهای مدل را از تنظیمات AutoMapper دریافت کرده و سپس بجای ویژگیهای خاصیت معادل ViewModel درخواست شده، بازگشت دهیم. به این ترتیب ASP.NET MVC تصور خواهد کرد که ViewModel ما نیز دقیقا دارای همان ویژگیهای Model است.
public static class AutoMapperExtensions { public static IEnumerable<Attribute> GetMappedAttributes( this IConfigurationProvider mapper, Type viewModelType, string viewModelPropertyName, IList<Attribute> existingAttributes) { if (viewModelType != null) { foreach (var typeMap in mapper.GetAllTypeMaps().Where(i => i.DestinationType == viewModelType)) { var propertyMaps = typeMap.GetPropertyMaps() .Where(propertyMap => !propertyMap.IsIgnored() && propertyMap.SourceMember != null) .Where(propertyMap => propertyMap.DestinationProperty.Name == viewModelPropertyName); foreach (var propertyMap in propertyMaps) { foreach (Attribute attribute in propertyMap.SourceMember.GetCustomAttributes(true)) { if (existingAttributes.All(i => i.GetType() != attribute.GetType())) { yield return attribute; } } } } } if (existingAttributes == null) { yield break; } foreach (var attribute in existingAttributes) { yield return attribute; } } }
ثبت تامین کنندههای سفارشی سازی شده توسط AutoMapper
پس از تهیهی تامین کنندههای انتقال ویژگیها، اکنون نیاز است آنها را به ASP.NET MVC معرفی کنیم:
protected void Application_Start() { AreaRegistration.RegisterAllAreas(); WebApiConfig.Register(GlobalConfiguration.Configuration); FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters); RouteConfig.RegisterRoutes(RouteTable.Routes); Mappings.RegisterMappings(); ModelMetadataProviders.Current = new MappedMetadataProvider(Mapper.Engine.ConfigurationProvider); var modelValidatorProvider = ModelValidatorProviders.Providers .Single(provider => provider is DataAnnotationsModelValidatorProvider); ModelValidatorProviders.Providers.Remove(modelValidatorProvider); ModelValidatorProviders.Providers.Add(new MappedValidatorProvider(Mapper.Engine.ConfigurationProvider)); }
در قسمت کار با ModelValidatorProviders.Providers، ابتدا صرفا همان تامین کنندهی از نوع DataAnnotationsModelValidatorProvider پیش فرض، یافت شده و حذف میشود. سپس تامین کنندهی سفارشی سازی شدهی خود را معرفی میکنیم تا جایگزین آن شود.
مثالی جهت آزمایش انتقال خودکار ویژگیهای مدل به ViewModel
کنترلر مثال برنامه به شرح زیر است. در اینجا از متد Mapper.Map جهت تبدیل خودکار مدل کاربر به ViewModel آن استفاده شدهاست:
public class HomeController : Controller { public ActionResult Index() { var model = new UserModel { FirstName = "و", Id = 1, LastName = "ن" }; var viewModel = Mapper.Map<UserViewModel>(model); return View(viewModel); } [HttpPost] public ActionResult Index(UserViewModel data) { return View(data); } }
@model Sample12.ViewModels.UserViewModel @using (Html.BeginForm("Index", "Home", FormMethod.Post, htmlAttributes: new { @class = "form-horizontal", role = "form" })) { <div class="row"> <div class="form-group"> @Html.LabelFor(d => d.FirstName, htmlAttributes: new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(d => d.FirstName) @Html.ValidationMessageFor(d => d.FirstName) </div> </div> <div class="form-group"> @Html.LabelFor(d => d.LastName, htmlAttributes: new { @class = "col-md-2 control-label" }) <div class="col-md-10"> @Html.TextBoxFor(d => d.LastName) @Html.ValidationMessageFor(d => d.LastName) </div> </div> <div class="form-group"> <div class="col-md-offset-2 col-md-10"> <input type="submit" value="ارسال" class="btn btn-default" /> </div> </div> </div> }
در این شکل هر چند نوع مدل View مورد استفاده از ViewModel ایی تامین شدهاست که دارای هیچ ویژگی و Data Annotations/Attributes نیست، اما برچسب هر فیلد از ویژگی Display دریافت شدهاست. همچنین اعتبارسنجی سمت کاربر فعال بوده و برچسبهای آنها نیز به درستی دریافت شدهاند.
کدهای کامل این مثال را از اینجا میتوانید دریافت کنید.
افزونهی زیر کار مشخص کردن کلماتی که قابل تبدیل از فارسی به پارسی هستند را انجام میدهد (به صورت خودکار و در زمان تایپ) و همچنین امکان تبدیل خودکار آنها را نیز فراهم میکند.
برای نصب آن باید به ترتیب زیر عمل کنید (مهم)
- نصب دات نت فریم ورک 3 و نیم (نزدیک به 231 مگابایت)
اگر VS.Net 2008 SP1 را دارید نیازی به این بسته نخواهید داشت.
- نصب بسته به روز رسانی سیستم (حدودا 14 مگ)
- نصب افزونه (حدودا 550 کیلوبایت)
لطفا این سه مرحله را به ترتیب انجام دهید در غیر اینصورت نتیجه نخواهید گرفت.
همچنین بدیهی است که برنامه باید با دسترسی admin نصب شود.
و همانطور که در عنوان این موضوع نیز ذکر گردید، این افزونه تنها برای MS-Word 2007 طراحی شده است.
سورس کامل و جزئیات نحوهی برنامه نویسی آنرا در طی روزهای آینده در این بلاگ مشاهده خواهید کرد.
پس از نصب به گزینهی word options مراجعه کنید: (جهت اطمینان از نصب آن)
این افزونه را باید در لیست مربوطه مشاهده نمائید: