بازخوردهای پروژه‌ها
نحوه تبدیل تاریخ میلادی به شمسی
با سلام و تشکر از پروژه عالی شما،
شما برای تبدیل تاریخ میلادی به شمسی از این کلاس استفاده کردید.
using Persia;

namespace Iris.Utilities.DateAndTime
{
    public class DateAndTime
    {
        public static DateTime GetDateTime()
        {
            return DateTime.Now;
        }

        public static string ConvertToPersian(DateTime dateTime, string mod = "")
        {
            SolarDate solar = Calendar.ConvertToPersian(dateTime);
            return string.IsNullOrEmpty(mod) ? solar.ToString() : solar.ToString(mod);
        }
    }
}
یک تابع استاتیک که تاریخ و مد استرینگ رو برای تبدیل میگیره و توسط یه DLL به نام Pershia تبدیل را انجام میده.
من یه روش دیگه میشناسم ( شاید شما هم بلد باشید)
استفاده از این کلاس
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;

namespace GSD.Globalization
{
    /// <summary>
    /// <Publisher>http://www.Sayan.ir</Publisher>
    /// <Author>Maziar Rezaie</Author>
    /// </summary>
    public class PersianCulture : CultureInfo
    {
        private readonly Calendar cal;
        private readonly Calendar[] optionals;

        /// <summary>
        /// کد رو بخوان تا بفهمی
        /// </summary>
        /// <param name="cultureName">fa-IR</param>
        /// <param name="useUserOverride">true</param>
        /// <remarks>لطفا در هنگام استفاده به سایت سایان اشاره کنید.</remarks>
        public PersianCulture()
            : this("fa-IR", true)
        {
        }

        public PersianCulture(string cultureName, bool useUserOverride)
            : base(cultureName, useUserOverride)
        {
            //Temporary Value for cal.
            cal = base.OptionalCalendars[0];

            //populating new list of optional calendars.
            var optionalCalendars = new List<Calendar>();
            optionalCalendars.AddRange(base.OptionalCalendars);
            optionalCalendars.Insert(0, new PersianCalendar());


            Type formatType = typeof(DateTimeFormatInfo);
            Type calendarType = typeof(Calendar);


            PropertyInfo idProperty = calendarType.GetProperty("ID", BindingFlags.Instance | BindingFlags.NonPublic);
            FieldInfo optionalCalendarfield = formatType.GetField("optionalCalendars",
                                                                  BindingFlags.Instance | BindingFlags.NonPublic);

            //populating new list of optional calendar ids
            var newOptionalCalendarIDs = new Int32[optionalCalendars.Count];
            for (int i = 0; i < newOptionalCalendarIDs.Length; i++)
                newOptionalCalendarIDs[i] = (Int32)idProperty.GetValue(optionalCalendars[i], null);

            optionalCalendarfield.SetValue(DateTimeFormat, newOptionalCalendarIDs);

            optionals = optionalCalendars.ToArray();
            cal = optionals[0];
            DateTimeFormat.Calendar = optionals[0];

            DateTimeFormat.MonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };
            DateTimeFormat.MonthGenitiveNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };
            DateTimeFormat.AbbreviatedMonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };
            DateTimeFormat.AbbreviatedMonthGenitiveNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", "" };


            DateTimeFormat.AbbreviatedDayNames = new string[] { "ی", "د", "س", "چ", "پ", "ج", "ش" };
            DateTimeFormat.ShortestDayNames = new string[] { "ی", "د", "س", "چ", "پ", "ج", "ش" };
            DateTimeFormat.DayNames = new string[] { "یکشنبه", "دوشنبه", "ﺳﻪشنبه", "چهارشنبه", "پنجشنبه", "جمعه", "شنبه" };

            DateTimeFormat.AMDesignator = "ق.ظ";
            DateTimeFormat.PMDesignator = "ب.ظ";

            /*
            DateTimeFormat.ShortDatePattern = "yyyy/MM/dd";
            DateTimeFormat.LongDatePattern = "yyyy/MM/dd";
            
            DateTimeFormat.SetAllDateTimePatterns(new[] {"yyyy/MM/dd"}, 'd');
            DateTimeFormat.SetAllDateTimePatterns(new[] {"dddd, dd MMMM yyyy"}, 'D');
            DateTimeFormat.SetAllDateTimePatterns(new[] {"yyyy MMMM"}, 'y');
            DateTimeFormat.SetAllDateTimePatterns(new[] {"yyyy MMMM"}, 'Y');
             */

        }

        public override Calendar Calendar
        {
            get { return cal; }
        }

        public override Calendar[] OptionalCalendars
        {
            get { return optionals; }
        }
    }
}

این کلاس به این صورت کار میکنه که در تمام برنامه‌ی شما تاریخ رو به صورت شمسی نشون میده ولی در ذخیره در بانک اطلاعاتی و کار در برنامه به صورت میلادی کار میکنه.
برای استفاده از اون هم باید به Application_BeginRequest  فایل Global.asax این کد‌ها رو اضافه کنید.   
using GSD.Globalization;
using System.Threading; 
 protected void Application_BeginRequest(object sender, EventArgs e)
        {
            var persianCulture = new PersianCulture();
            Thread.CurrentThread.CurrentCulture = persianCulture;
            Thread.CurrentThread.CurrentUICulture = persianCulture;
        }

اینم نمونه کد استفاه شده ازش WebApplication2.zip  
سوال من اینه روشی که شما استفاده کردید بهتره ؟ یا این روش؟ البته در روشی که من ارسال کردم به نظرم چون در Application_BeginRequest استفاده شده سر بار بیشتری داره! ولی همیشه تبدیل رو انجام میده و نیاز هیچ کد نویسی بیشتری نداره (مثل فراخوانی ConvertToPersian رو نیاز نداره)
نظر شما چیه؟
با تشکر از شما و صد تشکر بابت پروژه‌ی خوب و کاربردیتون

اشتراک‌ها
ساده ترین راه برای پیاده سازی GetHashCode

In .NET Core 2.1 a new struct was added. It’s called  System.HashCode  and it makes generating hash codes super convenient. Have a look at this class. 

class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }

public override int GetHashCode() => HashCode.Combine(FirstName, LastName);
}
ساده ترین راه برای پیاده سازی GetHashCode
نظرات مطالب
Blazor 5x - قسمت دهم - مبانی Blazor - بخش 7 - مسیریابی
بهبود قسمت «ارسال کوئری استرینگ‌ها به کامپوننت‌های مختلف » در Blazor 6x

در Blazor 6x دیگر نیازی نیست تا با استفاده از «HttpUtility.ParseQueryString»، کار پردازش دستی کوئری استرینگ‌های رسیده، صورت گیرد. برای نمونه فرض کنید Url رسیده، چنین شکلی را دارد:
/search?filter=some+stuff&page=3&assignee=User1&assignee=User2
برای دسترسی به مقادیر کوئری استرینگ‌های آدرس فوق، اینبار کافی است به صورت زیر عمل کنیم:
@code {
    [Parameter]
    [SupplyParameterFromQuery]
    public string Filter { get; set; }

    [Parameter]
    [SupplyParameterFromQuery]
    public int? Page { get; set; }

    [Parameter]
    [SupplyParameterFromQuery(Name = "assignee")]
    public string[] Assignees { get; set; }
}
یعنی ذکر دو ویژگی [Parameter, SupplyParameterFromQuery] بر روی یک خاصیت عمومی کامپوننت، آن‌را آماده‌ی دریافت مقادیر کوئری استرینگ‌های متناظری می‌کند. اگر می‌خواهید نام پارامتر، با نام کوئری استرینگ یکی نباشد (حالت پیش‌فرض)، از خاصیت Name این ویژگی می‌توان استفاده کرد.

نکته: در اینجا مقادیری مانند ذیل قابل قبول هستند:
String, bool, DateTime, decimal, double, float, Guid, int, long
و یا حالت Nullable آن‌ها و یا آرایه‌ای آن‌ها.

امکان ساخت Urlهایی به همراه کوئری استرینگ‌ها جهت هدایت ساده‌تر به آن‌ها

به Blazor 6x، متدهای الحاقی UriWithQueryParameter نیز اضافه شده‌اند و کار آن‌ها، افزودن ساده‌تر کوئری‌استرینگ‌ها به Urlها است که نمونه‌ای از آن به صورت زیر است:
var actualUri = NavigationManager.GetUriWithQueryParameters(new Dictionary<string, object>
        {
            ["full name"] = "John Doe", // Single value
            ["ping"] = new int?[] { 35, 16, null, 87, 240 }
        });
که چنین Url ای را تولید می‌کند (نمونه‌ای از روش تعریف کوئری استرینگ‌های آرایه‌ای):
host/?full%20name=John%20Doe&ping=35&ping=16&ping=87&ping=240
مثال‌های بیشتر آن‌را در اینجا می‌توانید مشاهده کنید.
مطالب
ASP.NET MVC #11

بررسی نکات تکمیلی Model binder در ASP.NET MVC

یک برنامه خالی جدید ASP.NET MVC را شروع کنید و سپس مدل زیر را به پوشه Models آن اضافه نمائید:

using System;

namespace MvcApplication7.Models
{
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }
public bool IsAdmin { set; get; }
}
}

از این مدل چند مقصود ذیل دنبال می‌شوند:
استفاده از Id به عنوان primary key برای edit و update رکوردها. استفاده از DateTime برای اینکه اگر کاربری اطلاعات بی ربطی را وارد کرد چگونه باید این مشکل را در حالت model binding خودکار تشخیص داد و استفاده از IsAdmin برای یادآوری یک نکته امنیتی بسیار مهم که اگر حین model binding خودکار به آن توجه نشود، سایت را با مشکلات حاد امنیتی مواجه خواهد کرد. سیستم پیشرفته است. می‌تواند به صورت خودکار ورودی‌های کاربر را تبدیل به یک شیء حاضر و آماده کند ... اما باید حین استفاده از این قابلیت دلپذیر به یک سری نکات امنیتی هم دقت داشت تا سایت ما به نحو دلپذیری هک نشود!

در ادامه یک کنترلر جدید به نام UserController را به پوشه کنترلرهای پروژه اضافه نمائید. همچنین نام کنترلر پیش فرض تعریف شده در قسمت مسیریابی فایل Global.asax.cs را هم به User تغییر دهید تا در هربار اجرای برنامه در VS.NET، نیازی به تایپ آدرس‌های مرتبط با UserController نداشته باشیم.
یک منبع داده تشکیل شده در حافظه را هم برای نمایش لیستی از کاربران، به نحو زیر به پروژه اضافه خواهیم کرد:

using System;
using System.Collections.Generic;

namespace MvcApplication7.Models
{
public class Users
{
public IList<User> CreateInMemoryDataSource()
{
return new[]
{
new User { Id = 1, Name = "User1", Password = "123", IsAdmin = false, AddDate = DateTime.Now },
new User { Id = 2, Name = "User2", Password = "456", IsAdmin = false, AddDate = DateTime.Now },
new User { Id = 3, Name = "User3", Password = "789", IsAdmin = true, AddDate = DateTime.Now }
};
}
}
}

در اینجا فعلا هدف آشنایی با زیر ساخت‌های ASP.NET MVC است و درک صحیح نحوه کارکرد آن. مهم نیست از EF استفاده می‌کنید یا NH یا حتی ADO.NET کلاسیک و یا از Micro ORMهایی که پس از ارائه دات نت 4 مرسوم شده‌اند. تهیه یک ToList یا Insert و Update با این فریم ورک‌ها خارج از بحث جاری هستند.

سورس کامل کنترلر User به شرح زیر است:

using System;
using System.Linq;
using System.Web.Mvc;
using MvcApplication7.Models;

namespace MvcApplication7.Controllers
{
public class UserController : Controller
{
[HttpGet]
public ActionResult Index()
{
var usersList = new Users().CreateInMemoryDataSource();
return View(usersList); // Shows the Index view.
}

[HttpGet]
public ActionResult Details(int id)
{
var user = new Users().CreateInMemoryDataSource().FirstOrDefault(x => x.Id == id);
if (user == null)
return View("Error");
return View(user); // Shows the Details view.
}

[HttpGet]
public ActionResult Create()
{
var user = new User { AddDate = DateTime.Now };
return View(user); // Shows the Create view.
}

[HttpPost]
public ActionResult Create(User user)
{
if (this.ModelState.IsValid)
{
// todo: Add record
return RedirectToAction("Index");
}
return View(user); // Shows the Create view again.
}

[HttpGet]
public ActionResult Edit(int id)
{
var user = new Users().CreateInMemoryDataSource().FirstOrDefault(x => x.Id == id);
if (user == null)
return View("Error");
return View(user); // Shows the Edit view.
}

[HttpPost]
public ActionResult Edit(User user)
{
if (this.ModelState.IsValid)
{
// todo: Edit record
return RedirectToAction("Index");
}
return View(user); // Shows the Edit view again.
}

[HttpPost]
public ActionResult Delete(int id)
{
// todo: Delete record
return RedirectToAction("Index");
}
}
}

توضیحات:

ایجاد خودکار فرم‌های ورود اطلاعات

در قسمت قبل برای توضیح دادن نحوه ایجاد فرم‌ها در ASP.NET MVC و همچنین نحوه نگاشت اطلاعات آن‌ها به اکشن متدهای کنترلرها، فرم‌های مورد نظر را دستی ایجاد کردیم.
اما باید درنظر داشت که برای ایجاد Viewها می‌توان از ابزار توکار خود VS.NET نیز استفاده کرد و سپس اطلاعات و فرم‌های تولیدی را سفارشی نمود. این سریع‌ترین راه ممکن است زمانیکه مدل مورد استفاده کاملا مشخص است و می‌خواهیم Strongly typed views را ایجاد کنیم.
برای نمونه بر روی متد Index کلیک راست کرده و گزینه Add view را انتخاب کنید. در اینجا گزینه‌ی create a strongly typed view را انتخاب کرده و سپس از لیست مدل‌ها، User را انتخاب نمائید. Scaffold template را هم بر روی حالت List قرار دهید.
برای متد Details هم به همین نحو عمل نمائید.
برای ایجاد View متناظر با متد Create در حالت HttpGet، تمام مراحل یکی است. فقط Scaffold template انتخابی را بر روی Create قرار دهید تا فرم ورود اطلاعات، به صورت خودکار تولید شود.
متد Create در حالت HttpPost نیازی به View اضافی ندارد. چون صرفا قرار است اطلاعاتی را از سرور دریافت و ثبت کند.
برای ایجاد View متناظر با متد Edit در حالت HttpGet، باز هم مراحل مانند قبل است با این تفاوت که Scaffold template انتخابی را بر روی گزینه Edit قرار دهید تا فرم ویرایش اطلاعات کاربر به صورت خودکار به پروژه اضافه شود.
متد Edit در حالت HttpPost نیازی به View اضافی ندارد و کارش تنها دریافت اطلاعات از سرور و به روز رسانی بانک اطلاعاتی است.
به همین ترتیب متد Delete نیز، نیازی به View خاصی ندارد. در اینجا بر اساس primary key دریافتی، می‌توان یک کاربر را یافته و حذف کرد.



سفارشی سازی Viewهای خودکار تولیدی

با کمک امکانات Scaffolding نامبرده شده، حجم قابل توجهی کد را در اندک زمانی می‌توان تولید کرد. بدیهی است حتما نیاز به سفارشی سازی کدهای تولیدی وجود خواهد داشت. مثلا شاید نیازی نباشد فیلد پسود کاربر، در حین نمایش لیست کاربران، نمایش داده شود. می‌شود کلا این ستون را حذف کرد و از این نوع مسایل.
یک مورد دیگر را هم در Viewهای تولیدی حتما نیاز است که ویرایش کنیم. آن هم مرتبط است به لینک حذف اطلاعات یک کاربر در صفحه Index.cshtml:

@Html.ActionLink("Delete", "Delete", new { id=item.Id }

در قسمت قبل هم عنوان شد که اعمال حذف باید بر اساس HttpPost محدود شوند تا بتوان میزان امنیت برنامه را بهبود داد. متد Delete هم در کنترلر فوق تنها به حالت HttpPost محدود شده است. بنابراین ActionLink پیش فرض را حذف کرده و بجای آن فرم و دکمه زیر را قرار می‌دهیم تا اطلاعات به سرور Post شوند:

@using (Html.BeginForm(actionName: "Delete", controllerName: "User", routeValues: new { id = item.Id }))
{
<input type="submit" value="Delete"
onclick="return confirm ('Do you want to delete this record?');" />
}

در اینجا نحوه ایجاد یک فرم، که id رکورد متناظر را به سرور ارسال می‌کند، مشاهده می‌کنید.



علت وجود دو متد، به ازای هر Edit یا Create

به ازای هر کدام از متدهای Edit و Create دو متد HttpGet و HttpPost را ایجاد کرده‌ایم. کار متدهای HttpGet نمایش View‌های متناظر به کاربر هستند. بنابراین وجود آن‌ها ضروری است. در این حالت چون از دو Verb متفاوت استفاده شده، می‌توان متدهای هم نامی را بدون مشکل استفاده کرد. به هر کدام از افعال Get و Post و امثال آن، یک Http Verb گفته می‌شود.



بررسی معتبر بودن اطلاعات دریافتی

کلاس پایه Controller که کنترلرهای برنامه از آن مشتق می‌شوند، شامل یک سری خواص و متدهای توکار نیز هست. برای مثال توسط خاصیت this.ModelState.IsValid می‌توان بررسی کرد که آیا Model دریافتی معتبر است یا خیر. برای بررسی این مورد، یک breakpoint را بر روی سطر this.ModelState.IsValid در متد Create قرار دهید. سپس به صفحه ایجاد کاربر جدید مراجعه کرده و مثلا بجای تاریخ روز، abcd را وارد کنید. سپس فرم را به سرور ارسال نمائید. در این حالت مقدار خاصیت this.ModelState.IsValid مساوی false می‌باشد که حتما باید به آن پیش از ثبت اطلاعات دقت داشت.



شبیه سازی عملکرد ViewState در ASP.NET MVC

در متدهای Create و Edit در حالت Post، اگر اطلاعات Model معتبر نباشند، مجددا شیء User دریافتی، به View بازگشت داده می‌شود. چرا؟
صفحات وب، زمانیکه به سرور ارسال می‌شوند، تمام اطلاعات کنترل‌های خود را از دست خواهد داد (صفحه پاک می‌شود، چون مجددا یک صفحه خالی از سرور دریافت خواهد شد). برای رفع این مشکل در ASP.NET Web forms، از مفهومی به نام ViewState کمک می‌گیرند. کار ViewState ذخیره موقت اطلاعات فرم جاری است برای استفاده مجدد پس از Postback. به این معنا که پس از ارسال فرم به سرور، اگر کاربری در textbox اول مقدار abc را وارد کرده بود، پس از نمایش مجدد فرم، مقدار abc را در همان textbox مشاهده خواهد کرد (شبیه سازی برنامه‌های دسکتاپ در محیط وب). بدیهی است وجود ViewState برای ذخیره سازی این نوع اطلاعات، حجم صفحه را بالا می‌برد (بسته به پیچیدگی صفحه ممکن است به چند صد کیلوبایت هم برسد).
در ASP.NET MVC بجای استفاده از ترفندی به نام ViewState، مجددا اطلاعات همان مدل متناظر با View را بازگشت می‌دهند. در این حالت پس از ارسال صفحه به سرور و نمایش مجدد صفحه ورود اطلاعات، تمام کنترل‌ها با همان مقادیر قبلی وارد شده توسط کاربر قابل مشاهده خواهند بود (مدل مشخص است، View ما هم از نوع strongly typed می‌باشد. در این حالت فریم ورک می‌داند که اطلاعات را چگونه به کنترل‌های قرار گرفته در صفحه نگاشت کند).
در مثال فوق، اگر اطلاعات وارد شده صحیح باشند، کاربر به صفحه Index هدایت خواهد شد. در غیراینصورت مجددا همان View جاری با همان اطلاعات model قبلی که کاربر تکمیل کرده است به او برای تصحیح، نمایش داده می‌شود. این مساله هم جهت بالا بردن سهولت کاربری برنامه بسیار مهم است. تصور کنید که یک فرم خالی با پیغام «تاریخ وارد شده معتبر نیست» مجدا به کاربر نمایش داده شود و از او درخواست کنیم که تمام اطلاعات دیگر را نیز از صفر وارد کند چون اطلاعات صفحه پس از ارسال به سرور پاک شده‌اند؛ که ... اصلا قابل قبول نیست و فوق‌العاده برنامه را غیرحرفه‌ای نمایش می‌دهد.



خطاهای نمایش داده شده به کاربر

به صورت پیش فرض خطایی که به کاربر نمایش داده می‌شود، استثنایی است که توسط فریم ورک صادر شده است. برای مثال نتوانسته است abcd را به یک تاریخ معتبر تبدیل کند. می‌توان توسط this.ModelState.AddModelError خطایی را نیز در اینجا اضافه کرد و پیغام بهتری را به کاربر نمایش داد. یا توسط یک سری data annotations هم کار اعتبار سنجی را سفارشی کرد که بحث آن به صورت جداگانه در یک قسمت مستقل بررسی خواهد شد.
ولی به صورت خلاصه اگر به فرم‌های تولید شده توسط VS.NET دقت کنید، در ابتدای هر فرم داریم:

@Html.ValidationSummary(true)

در اینجا خطاهای عمومی در سطح مدل نمایش داده می‌شوند. برای اضافه کردن این نوع خطاها، در متد AddModelError، مقدار key را خالی وارد کنید:

ModelState.AddModelError(string.Empty, "There is something wrong with model.");

همچنین در این فرم‌ها داریم:
@Html.EditorFor(model => model.AddDate)
@Html.ValidationMessageFor(model => model.AddDate)

EditorFor سعی می‌کند اندکی هوش به خرج دهد. یعنی اگر خاصیت دریافتی مثلا از نوع bool بود، خودش یک checkbox را در صفحه نمایش می‌دهد. همچنین بر اساس متادیتا یک خاصیت نیز می‌تواند تصمیم گیری را انجام دهد. این متادیتا منظور attributes و data annotations ایی است که به خواص یک مدل اعمال می‌شود. مثلا اگر ویژگی HiddenInput را به یک خاصیت اعمال کنیم، به شکل یک فیلد مخفی در صفحه ظاهر خواهد شد.
یا متد Html.DisplayFor، اطلاعات را به صورت فقط خواندنی نمایش می‌دهد. اصطلاحا به این نوع متدها، Templated Helpers هم گفته می‌شود. بحث بیشتر درباره‌ای این موارد به قسمتی مجزا و مستقل موکول می‌گردد. برای نمونه کل فرم ادیت برنامه را حذف کنید و بجای آن بنویسید Html.EditorForModel و سپس برنامه را اجرا کنید. یک فرم کامل خودکار ویرایش اطلاعات را مشاهده خواهید کرد (و البته نکات سفارشی سازی آن به یک قسمت کامل نیاز دارند).
در اینجا متد ValidationMessageFor کار نمایش خطاهای اعتبارسنجی مرتبط با یک خاصیت مشخص را انجام می‌دهد. بنابراین اگر قصد ارائه خطایی سفارشی و مخصوص یک فیلد مشخص را داشتید، در متد AddModelError، مقدار پارامتر اول یا همان key را مساوی نام خاصیت مورد نظر قرار دهید.


مقابله با مشکل امنیتی Mass Assignment در حین کار با Model binders

استفاده از Model binders بسیار لذت بخش است. یک شیء را به عنوان پارامتر اکشن متد خود معرفی می‌کنیم. فریم ورک هم در ادامه سعی می‌کند تا اطلاعات فرم را به خواص این شیء نگاشت کند. بدیهی است این روش نسبت به روش ASP.NET Web forms که باید به ازای تک تک کنترل‌های موجود در صفحه یکبار کار دریافت اطلاعات و مقدار دهی خواص یک شیء را انجام داد، بسیار ساده‌تر و سریعتر است.
اما اگر همین سیستم پیشرفته جدید ناآگاهانه مورد استفاده قرار گیرد می‌تواند منشاء حملات ناگواری شود که به نام «Mass Assignment» شهرت یافته‌اند.
همان صفحه ویرایش اطلاعات را درنظر بگیرید. چک باکس IsAdmin قرار است در قسمت مدیریتی برنامه تنظیم شود. اگر کاربری نیاز داشته باشد اطلاعات خودش را ویرایش کند، مثلا پسوردش را تغییر دهد، با یک صفحه ساده کلمه عبور قبلی را وارد کنید و دوبار کلمه عبور جدید را نیز وارد نمائید، مواجه خواهد شد. خوب ... اگر همین کاربر صفحه را جعل کند و فیلد چک باکس IsAdmin را به صفحه اضافه کند چه اتفاقی خواهد افتاد؟ بله ... مشکل هم همینجا است. در اینصورت کاربر عادی می‌تواند دسترسی خودش را تا سطح ادمین بالا ببرد، چون model binder اطلاعات IsAdmin را از کاربر دریافت کرده و به صورت خودکار به model ارائه شده، نگاشت کرده است.
برای مقابله با این نوع حملات چندین روش وجود دارند:
الف) ایجاد لیست سفید
به کمک ویژگی Bind می‌توان لیستی از خواص را جهت به روز رسانی به model binder معرفی کرد. مابقی ندید گرفته خواهند شد:

public ActionResult Edit([Bind(Include = "Name, Password")] User user)

در اینجا تنها خواص Name و Password توسط model binder به خواص شیء User نگاشت می‌شوند.
به علاوه همانطور که در قسمت قبل نیز ذکر شد، متد edit را به شکل زیر نیز می‌توان بازنویسی کرد. در اینجا متدهای توکار UpdateModel و TryUpdateModel نیز لیست سفید خواص مورد نظر را می‌پذیرند (اعمال دستی model binding):

[HttpPost]
public ActionResult Edit()
{
var user = new User();
if(TryUpdateModel(user, includeProperties: new[] { "Name", "Password" }))
{
// todo: Edit record
return RedirectToAction("Index");
}
return View(user); // Shows the Edit view again.
}


ب) ایجاد لیست سیاه
به همین ترتیب می‌توان تنها خواصی را معرفی کرد که باید صرفنظر شوند:
public ActionResult Edit([Bind(Exclude = "IsAdmin")] User user)

در اینجا از خاصیت IsAdmin صرف نظر گردیده و از مقدار ارسالی آن توسط کاربر استفاده نخواهد شد.
و یا می‌توان پارامتر excludeProperties متد TryUpdateModel را نیز مقدار دهی کرد.

لازم به ذکر است که ویژگی Bind را به کل یک کلاس هم می‌توان اعمال کرد. برای مثال:

using System;
using System.Web.Mvc;

namespace MvcApplication7.Models
{
[Bind(Exclude = "IsAdmin")]
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }
public bool IsAdmin { set; get; }
}
}

این مورد اثر سراسری داشته و قابل بازنویسی نیست. به عبارتی حتی اگر در متدی خاصیت IsAdmin را مجددا الحاق کنیم، تاثیری نخواهد داشت.
یا می‌توان از ویژگی ReadOnly هم استفاده کرد:
using System;
using System.ComponentModel;

namespace MvcApplication7.Models
{
public class User
{
public int Id { set; get; }
public string Name { set; get; }
public string Password { set; get; }
public DateTime AddDate { set; get; }

[ReadOnly(true)]
public bool IsAdmin { set; get; }
}
}

در این حالت هم خاصیت IsAdmin هیچگاه توسط model binder به روز و مقدار دهی نخواهد شد.

ج) استفاده از ViewModels
این راه حلی است که بیشتر مورد توجه معماران نرم افزار است و البته کسانی که پیشتر با الگوی MVVM کار کرده باشند این نام برایشان آشنا است؛ اما در اینجا مفهوم متفاوتی دارد. در الگوی MVVM، کلاس‌های ViewModel شبیه به کنترلرها در MVC هستند یا به عبارتی همانند رهبر یک اکستر عمل می‌کنند. اما در الگوی MVC خیر. در اینجا فقط مدل یک View هستند و نه بیشتر. هدف هم این است که بین Domain Model و View Model تفاوت قائل شد.
کار View model در الگوی MVC، شکل دادن به چندین domain model و همچنین اطلاعات اضافی دیگری که نیاز هستند، جهت استفاده نهایی توسط یک View می‌باشد. به این ترتیب View با یک شیء سر و کار خواهد داشت و همچنین منطق شکل دهی به اطلاعات مورد نیازش هم از داخل View حذف شده و به خواص View model در زمان تشکیل آن منتقل می‌شود.
مشخصات یک View model خوب به شرح زیر است:
الف) رابطه بین یک View و View model آن، رابطه‌ای یک به یک است. به ازای هر View، بهتر است یک کلاس View model وجود داشته باشد.
ب) View ساختار View model را دیکته می‌کند و نه کنترلر.
ج) View modelها صرفا یک سری کلاس POCO (کلاس‌هایی تشکیل شده از خاصیت، خاصیت، خاصیت ....) هستند که هیچ منطقی در آن‌ها قرار نمی‌گیرد.
د) View model باید حاوی تمام اطلاعاتی باشد که View جهت رندر نیاز دارد و نه بیشتر و الزامی هم ندارد که این اطلاعات مستقیما به domain models مرتبط شوند. برای مثال اگر قرار است firstName+LastName در View نمایش داده شود، کار این جمع زدن باید حین تهیه View Model انجام شود و نه داخل View. یا اگر قرار است اطلاعات عددی با سه رقم جدا کننده به کاربر نمایش داده شوند، وظیفه View Model است که یک خاصیت اضافی را برای تهیه این مورد تدارک ببیند. یا مثلا اگر یک فرم ثبت نام داریم و در این فرم لیستی وجود دارد که تنها Id عنصر انتخابی آن در Model اصلی مورد استفاده قرار می‌گیرد، تهیه اطلاعات این لیست هم کار ViewModel است و نه اینکه مدام به Model اصلی بخواهیم خاصیت اضافه کنیم.

ViewModel چگونه پیاده سازی می‌شود؟
اکثر مقالات را که مطالعه کنید، این روش را توصیه می‌کنند:

public class MyViewModel
{
    public SomeDomainModel1 Model1 { get; set; }
    public SomeDomainModel2 Model2 { get; set; }
    ...
}

یعنی اینکه View ما به اطلاعات مثلا دو Model نیاز دارد. این‌ها را به این شکل محصور و کپسوله می‌کنیم. اگر View، واقعا به تمام فیلدهای این کلاس‌ها نیاز داشته باشد، این روش صحیح است. در غیر اینصورت، این روش نادرست است (و متاسفانه همه جا هم دقیقا به این شکل تبلیغ می‌شود).
ViewModel محصور کننده یک یا چند مدل نیست. در اینجا حس غلط کار کردن با یک ViewModel را داریم. ViewModel فقط باید ارائه کننده اطلاعاتی باشد که یک View نیاز دارد و نه بیشتر و نه تمام خواص تمام کلاس‌های تعریف شده. به عبارتی این نوع تعریف صحیح است:

public class MyViewModel
{
    public string SomeExtraField1 { get; set; }
    public string SomeExtraField2 { get; set; }
public IEnumerable<SelectListItem> StateSelectList { get; set; }
// ...
    public string PersonFullName { set; set; }
}

در اینجا، View متناظری، قرار است نام کامل یک شخص را به علاوه یک سری اطلاعات اضافی که در domain model نیست، نمایش دهد. مثلا نمایش نام استان‌ها که نهایتا Id انتخابی آن قرار است در برنامه استفاده شود.
خلاصه علت وجودی ViewModel این موارد است:
الف) Model برنامه را مستقیما در معرض استفاده قرار ندهیم (عدم رعایت این نکته به مشکلات امنیتی حادی هم حین به روز رسانی اطلاعات ممکن است ختم ‌شود که پیشتر توضیح داده شد).
ب) فیلدهای نمایشی اضافی مورد نیاز یک View را داخل Model برنامه تعریف نکنیم (مثلا تعاریف عناصر یک دراپ داون لیست، جایش اینجا نیست. مدل فقط نیاز به Id عنصر انتخابی آن دارد).

با این توضیحات، اگر View به روز رسانی اطلاعات کلمه عبور کاربر، تنها به اطلاعات id آن کاربر و کلمه عبور او نیاز دارد، فقط باید همین اطلاعات را در اختیار View قرار داد و نه بیشتر:

namespace MvcApplication7.Models
{
public class UserViewModel
{
public int Id { set; get; }
public string Password { set; get; }
}
}

به این ترتیب دیگر خاصیت IsAdming اضافه‌ای وجود ندارد که بخواهد مورد حمله واقع شود.



استفاده از model binding برای آپلود فایل به سرور

برای آپلود فایل به سرور تنها کافی است یک اکشن متد به شکل زیر را تعریف کنیم. HttpPostedFileBase نیز یکی دیگر از model binderهای توکار ASP.NET MVC است:

[HttpGet]
public ActionResult Upload()
{
return View(); // Shows the upload page
}

[HttpPost]
public ActionResult Upload(System.Web.HttpPostedFileBase file)
{
string filename = Server.MapPath("~/files/somename.ext");
file.SaveAs(filename);
return RedirectToAction("Index");
}

View متناظر هم می‌تواند به شکل زیر باشد:

@{
ViewBag.Title = "Upload";
}
<h2>
Upload</h2>
@using (Html.BeginForm(actionName: "Upload", controllerName: "User",
method: FormMethod.Post,
htmlAttributes: new { enctype = "multipart/form-data" }))
{
<text>Upload a photo:</text> <input type="file" name="photo" />
<input type="submit" value="Upload" />
}

اگر دقت کرده باشید در طراحی ASP.NET MVC از anonymously typed objects زیاد استفاده می‌شود. در اینجا هم برای معرفی enctype فرم آپلود، مورد استفاده قرار گرفته است. به عبارتی هر جایی که مشخص نبوده چه تعداد ویژگی یا کلا چه ویژگی‌ها و خاصیت‌هایی را می‌توان تنظیم کرد، اجازه تعریف آن‌ها را به صورت anonymously typed objects میسر کرده‌اند. یک نمونه دیگر آن در متد routes.MapRoute فایل Global.asax.cs است که پارامتر سوم دریافت مقدار پیش فرض‌ها نیز anonymously typed object است. یا نمونه دیگر آن‌را در همین قسمت در جایی که لینک delete را به فرم تبدیل کردیم مشاهده نمودید. مقدار routeValues هم یک anonymously typed object معرفی شد.



سفارشی سازی model binder پیش فرض ASP.NET MVC

در همین مثال فرض کنید تاریخ را به صورت شمسی از کاربر دریافت می‌کنیم. خاصیت تعریف شده هم DateTime میلادی است. به عبارتی model binder حین تبدیل رشته تاریخ شمسی دریافتی به تاریخ میلادی با شکست مواجه شده و نهایتا خاصیت this.ModelState.IsValid مقدارش false خواهد بود. برای حل این مشکل چکار باید کرد؟
برای این منظور باید نحوه پردازش یک نوع خاص را سفارشی کرد. ابتدا با پیاده سازی اینترفیس IModelBinder شروع می‌کنیم. توسط bindingContext.ValueProvider می‌توان به مقداری که کاربر وارد کرده در میانه راه دسترسی یافت. آن‌را تبدیل کرده و نمونه صحیح را بازگشت داد.
نمونه‌ای از این پیاده سازی را در ادامه ملاحظه می‌کنید:

using System;
using System.Globalization;
using System.Web.Mvc;

namespace MvcApplication7.Binders
{
public class PersianDateModelBinder : IModelBinder
{

public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
var valueResult = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);
var modelState = new ModelState { Value = valueResult };
object actualValue = null;
try
{
var parts = valueResult.AttemptedValue.Split('/'); //ex. 1391/1/19
if (parts.Length != 3) return null;
int year = int.Parse(parts[0]);
int month = int.Parse(parts[1]);
int day = int.Parse(parts[2]);
actualValue = new DateTime(year, month, day, new PersianCalendar());
}
catch (FormatException e)
{
modelState.Errors.Add(e);
}

bindingContext.ModelState.Add(bindingContext.ModelName, modelState);
return actualValue;
}
}
}

سپس برای معرفی PersianDateModelBinder جدید تنها کافی است سطر زیر را

ModelBinders.Binders.Add(typeof(DateTime), new PersianDateModelBinder());

به متد Application_Start قرار گرفته در فایل Global.asax.cs برنامه اضافه کرد. از این پس کاربران می‌توانند تاریخ‌ها را در برنامه شمسی وارد کنند و model binder بدون مشکل خواهد توانست اطلاعات ورودی را به معادل DateTime میلادی آن تبدیل کند و استفاده نماید.
تعریف مدل بایندر سفارشی در فایل Global.asax.cs آن‌را به صورت سراسری در تمام مدل‌ها و اکشن‌متدها فعال خواهد کرد. اگر نیاز بود تنها یک اکشن متد خاص از این مدل بایندر سفارشی استفاده کند می‌توان به روش زیر عمل کرد:

public ActionResult Create([ModelBinder(typeof(PersianDateModelBinder))] User user)

همچنین ویژگی ModelBinder را به یک کلاس هم می‌توان اعمال کرد:

[ModelBinder(typeof(PersianDateModelBinder))]
public class User
{


مطالب
پیاده سازی Template تو در تو در AngularJS و ASP.NET MVC
در Angular می شود یک سری Template و ساختار از پیش تعریف شده داشت و در هر زمان که نیاز بود مدلی را به آنها پاس داد و نمای HTML مورد نظر را تحویل گرفت.
بطور مثال در فرم ساز‌ها یا همان فرم‌های داینامیک ما نیاز داریم که مدل یک فرم (مثلا در فرمت JSON) را برای View ارسال کنیم و با استفاده از توانایی‌های Angular بتوانیم فرم مورد نظر را نمایش دهیم و در صورت امکان تغییر دهیم.
ViewModel فرم شما در MVC میتواند چیزی شبیه این باشد
   public class Form
    {
        public string Name { get; set; }
        public string Title { get; set; }
        public List<BaseElement> Elements { get; set; }
    }

    public abstract class BaseElement
    {
        public string Name { get; set; }
        public string Title { get; set; }
    }
    public class Section : BaseElement
    {
        public List<TextBox> Elements { get; set; }
    }
    public class TextBox : BaseElement
    {
        public string Value { get; set; }
        public string CssClass { get; set; }
    }
یک کنترلر هم برای مدیریت فرم ایجاد میکنیم
  public class FormBuilderController : Controller
    {
        //
        // GET: /FormBuilder/

        public ActionResult Index()
        {
            var form = new Form();
            var section = new Section() { Title = "Basic Info", Name = "section01" };
            section.Elements.Add(new TextBox() { Name = "txt1", Title = "First Text Box" });
            form.Elements.Add(new TextBox() { Name = "txt1", Title = "Second Text Box" });
            var formJson=JsonConvert.SerializeObject(form);
            return View(formJson);
        }
    }
در این کنترلر ما تنها یک اکشن داریم که در آن یک فرم خام ساده ایجاد کرده و سپس با استفاده از کتابخانه Json.net آنرا سریال و تبدیل به فرمت Json می‌کنیم و سپس آنرا برای View ایی که از Angular قدرت گرفته است، ارسال می‌نمائیم.
پیاده سازی View با Angular به اشکال گوناگونی قابل پیاده سازی و استفاده است که در اینجا و اینجا  می‌توانید ببینید.
 اما برای اینکه مشکل کنترلرهای تودرتو(Section) را حل کنید باید بصورت بازگشتی Template را فراخوانی کنید.
  <script type="text/ng-template" id="ElementTemplate">  
    <div ng-if="control.Type == 'JbSection'">
    <h2>{{control.Title}}</h2>
    <ul>
        <li ng-repeat="control in control.Elements" ng-include="'ElementTemplate'"></li>
    </ul>
    </div>
    </script>
و یا
<script type="text/ng-template" id="element.html">
    {{data.label}}
    <ul>
        <li ng-repeat="element in data.elements" ng-include="'element.html'"></li>
    </ul>
</script>

<ul ng-controller="NestedFormCtrl">
    <li ng-repeat="field in formData" ng-include="'element.html'"></li>
</ul>
در اینجا صفحه element.html یک صفحه بیرونی است که Template ما در آن قرار دارد.
مطالب
استفاده از EF7 با پایگاه داده SQLite تحت NET Core. به کمک Visual Studio Code
در این مقاله سعی داریم مراحل نوشتن و اجرای یک برنامه‌ی ساده را تحت NET Core. و با بهره گیری از دیتابیس SQLite و EF7، دنبال کنیم. همچنین از آنجایی‌که NET Core. به صورت چندسکویی طراحی شده‌است و تحت لینوکس و مکینتاش هم قابل اجراست، در نتیجه مناسب دیدم که ابزار نوشتن این پروژه‌ی ساده نیز قابلیت چندسکویی داشته و تحت لینوکس و مکینتاش نیز قابل اجرا باشد. در نتیجه به جای Visual Studio در این مقاله از Visual Studio Code استفاده شده است.

ابزارهای پیش نیاز:
  1. Visual Studio Code
  2. .NET Core
در اولین قدم، برنامه‌ی متن باز Visual Studio Code را از اینجا دانلود و نصب کنید. برنامه‌ی Visual Studio Code که در ادامه‌ی فعالیت‌های جدید متن باز مایکروسافت به بازار عرضه شده است، سریع، سبک و کاملا قابل توسعه و سفارشی سازی است و از اکثر زبان‌های معروف پشتیبانی می‌کند.
در قدم بعدی، شما باید NET Core. را از اینجا (64 بیتی) دانلود و نصب کنید.

 .NET Core چیست؟
NET Core. در واقع پیاده سازی بخشی از NET. اصلی است که به صورت متن باز در حال توسعه می‌باشد و بر روی لینوکس و مکینتاش هم قابل اجراست. موتور اجرای دات نت کامل CLR نام دارد و NET Core. نیز دارای موتور اجرایی CoreCLR است و شامل فریمورک CoreFX می‌باشد.

در حال حاضر شما می‌توانید با استفاده از NET Core. برنامه‌های کنسولی و تحت وب با ASP.NET 5 بنویسید و احتمالا در آینده می‌توان امیدوار بود که از ساختارهای پیچیده‌تری مثل WPF نیز پشتیبانی کند.

پس از آنکه NET Core. را دانلود و نصب کردید، جهت شروع پروژه، یک پوشه را در یکی از درایوها ساخته (در این مثال E:\Projects\EF7-SQLite-NETCore) و Command prompt را در آنجا باز کنید. سپس دستورات زیر را به ترتیب اجرا کنید:

dotnet new
dotnet restore
dotnet run

دستور dotnet new یک پروژه‌ی ساده‌ی Hello World را در پوشه‌ی جاری ایجاد می‌کند که حاوی فایل‌های زیر است:
  • NuGet.Config (این فایل، تنظیمات مربوط به نیوگت را جهت کشف و دریافت وابستگی‌های پروژه، شامل می‌شود)
  •  Program.cs (این فایل سی شارپ حاوی کد برنامه است)
  • project.json (این فایل حاوی اطلاعات پلتفرم هدف و لیست وابستگی‌های پروژه است)

دستور dotnet restore بر اساس لیست وابستگی‌ها و پلتفرم هدف، وابستگی‌های لازم را از مخزن نیوگت دریافت می‌کند. (در صورتی که در هنگام اجرای این دستور با خطای NullReferenceException مواجه شدید از دستور dnu restore استفاده کنید. این خطا در گیت هاب در حال بررسی است)

دستور dotnet run هم سورس برنامه را کامپایل و اجرا می‌کند. در صورتی که پیام Hello World را مشاهده کردید، یعنی برنامه‌ی شما تحت NET Core. با موفقیت اجرا شده است.

توسعه‌ی پروژه با Visual Studio Code

در ادامه، قصد داریم پروژه‌ی HelloWorld را تحت Visual Studio Code باز کرده و تغییرات بعدی را در آنجا اعمال کنیم. پس از باز کردن Visual Studio Code از منوی File گزینه‌ی Open Folder را انتخاب کنید و پوشه‌ی حاوی پروژه (EF7-SQLite-NETCore) را انتخاب کنید. اکنون پروژه‌ی شما تحت VS Code باز شده و قابل ویرایش است.

سپس از لیست فایل‌های پروژه، فایل project.json را باز کرده و در بخش "dependencies" یک ردیف را برای EntityFramework.SQLite به صورت زیر اضافه کنید. به محض افزودن این خط در project.json و ذخیره‌ی آن، در صورتیکه قبلا این وابستگی دریافت نشده باشد، Visual Studio Code با نمایش یک هشدار در بالای برنامه به شما امکان دریافت اتوماتیک این وابستگی را می‌دهد. در نتیجه کافیست دکمه‌ی Restore را زده و منتظر شوید تا وابستگی EntityFramework.SQLite از مخزن ناگت دانلود و برای پروژه‌ی شما تنظیم شود.

"EntityFramework.SQLite": "7.0.0-rc1-final"

دریافت اتوماتیک وابستگی‌ها توسط Visual Studio Code


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

اکنون همه‌ی موارد، جهت توسعه‌ی پروژه آماده است. ماوس خود را بر روی ریشه‌ی پروژه در VS Code قرار داده و New Folder را انتخاب کنید و نام Models را برای آن تایپ کنید. این پوشه قرار است مدل کلاس‌های پروژه را شامل شود. در اینجا ما یک مدل به نام Book داریم و نام کانتکست اصلی پروژه را هم LibraryContext گذاشته‌ایم.

بر روی پوشه‌ی Models راست کلیک کرده و گزینه‌ی New File را انتخاب کنید. سپس فایل‌های Book.cs و LibraryContext.cs را ایجاد کرده و کدهای زیر را برای مدل و کانتکست، در درون این دو فایل قرار دهید.

Book.cs

namespace Models
{
    public class Book
    {
        public int ID { get; set; }
        public string Title { get; set; }
        public string Author{get;set;}
        public int PublishYear { get; set; }
    }
}
LibraryContext.cs
using Microsoft.Data.Entity;
using Microsoft.Data.Sqlite;

namespace Models
{
    public class LibraryContext : DbContext
    {
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            var connectionStringBuilder = new SqliteConnectionStringBuilder { DataSource = "test.db" };
            var connectionString = connectionStringBuilder.ToString();
            var connection = new SqliteConnection(connectionString);
            optionsBuilder.UseSqlite(connection);
        }
        public DbSet<Book> Books { get; set; }
    }
}
در فایل Book.cs یک مدل ساده به نام Book داریم که حاوی اطلاعات یک کتاب است. فایل LibraryContext.cs نیز حاوی کلاس LibraryContext است که یک مجموعه از کتاب‌ها را با نام Books نگهداری می‌کند. در متد OnConfiguring تنظیمات لازم را جهت استفاده از دیتابیس SQLite با نام test.db، قرار داده‌ایم که البته این کد را در پروژه‌های کامل می‌توان در خارج از LibraryContext قرار داد تا بتوان مسیر ذخیره سازی دیتابیس را کنترل و قابل تنظیم کرد.

در قدم آخر هم کافیست که فایل Program.cs را تغییر دهید و مقادیری را در دیتابیس ذخیره و بازخوانی کنید.
Program.cs
using System;
using Models;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            Console.WriteLine("EF7 + Sqlite with the taste of .NET Core");

            try
            {
                using (var context = new LibraryContext())
                {
                    context.Database.EnsureCreated();

                    var book1 = new Book()
                    {
                        Title = "Adaptive Code via C#: Agile coding with design patterns and SOLID principles ",
                        Author = "Gary McLean Hall",
                        PublishYear = 2014
                    };

                    var book2 = new Book()
                    {
                        Title = "CLR via C# (4th Edition)",
                        Author = "Jefrey Ritcher",
                        PublishYear = 2012
                    };

                    context.Books.Add(book1);
                    context.Books.Add(book2);

                    context.SaveChanges();

                    ReadData(context);
                }

                Console.WriteLine("Press any key to exit ...");
                Console.ReadKey();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"An exception occured: {ex.Message}\n{ex.StackTrace}");
            }
        }

        private static void ReadData(LibraryContext context)
        {
            Console.WriteLine("Books in database:");
            foreach (var b in context.Books)
            {
                Console.WriteLine($"Book {b.ID}");
                Console.WriteLine($"\tName: {b.Title}");
                Console.WriteLine($"\tAuthor: {b.Author}");
                Console.WriteLine($"\tPublish Year: {b.PublishYear}");
                Console.WriteLine();
            }
        }
    }
}
در فایل Program.cs جهت تست پروژه، از روی کلاس Book دو نمونه ساخته و آن‌ها را به دیتابیس افزوده و ذخیره می‌کنیم و در انتها، اطلاعات تمامی کتاب‌های موجود در دیتابیس را با جزییات نمایش می‌دهیم.

جهت اجرای برنامه کافیست Command prompt را در آدرس پروژه باز کرده و دستور dotnet run را اجرا کنید. پروژه‌ی شما کامپایل و اجرا می‌شود و خروجی مشابه زیر را مشاهده خواهید کرد. اگر برنامه را مجددا اجرا کنید، به جای دو کتاب اطلاعات چهار کتاب نمایش داده خواهد شد؛ چرا که در هر مرحله اطلاعات دو کتاب در دیتابیس درج می‌شود.

.NET Core + EF7 + SQLite

اگر به پوشه‌ی bin که در پوشه‌ی پروژه ایجاد شده است، نگاهی بیندازید، خبری از فایل باینری نیست. چرا که در لحظه‌، تولید و اجرا شده است. جهت build کردن پروژه و تولید فایل باینری کافیست دستور dotnet build را اجرا کنید، تا فایل باینری در پوشه‌ی bin ایجاد شود.

جهت انتشار برنامه می‌توانید دستور dotnet publish را اجرا کنید. این دستور نه تنها برنامه، که تمام وابستگی‌های مورد نیاز آن را برای اجرای در یک پلتفرم خاص تولید می‌کند. برای مثال بعد از اجرای این دستور یک پوشه‌ی win7-x64 حاوی 211 فایل در مجموع تولید شده است که تمامی وابستگی‌های این پروژه را شامل می‌شود.

Publishing .NET Core app

در واقع این پوشه تمام وابستگی‌های مورد نیاز پروژه را همراه خود دارد و در نتیجه جهت اجرای این برنامه برخلاف برنامه‌های معمولی دات نت، دیگر نیازی به نصب هیچ وابستگی مجزایی نیست و حتی پروژه‌های نوشته شده تحت NET Core. را می‌توانید در سیستم‌های عامل‌های دیگری مثل لینوکس و مکینتاش و یا  Windows IoT بر روی سخت افزار Raspberry Pi 2 هم اجرا کنید.

جهت مطالعه‌ی بیشتر:
نظرات مطالب
شروع به کار با EF Core 1.0 - قسمت 13 - بررسی سیستم ردیابی تغییرات
یک نکته‌ی تکمیلی: روش نگاشت خواص سایه‌ای توسط AutoMapper
    public static class ShadowProperty
    {
        public static readonly string CreatedAt = nameof(CreatedAt);
    }

    public class Student
    {
        public int Id { get; set; }
        public string Name { get; set; }
    }

    public class StudentDto
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public DateTimeOffset Created { get; set; }
    }

    public class StudentMappingProfile : Profile
    {
        public StudentMappingProfile()
        {
            CreateMap<Student, StudentDto>()
                .ForMember(
                    dest => dest.Created,
                    opt => opt.MapFrom(src => EF.Property<DateTimeOffset>(src, ShadowProperty.CreatedAt))
                );
        }
    }
و بعد هم کوئری گرفتن از آن:
var students = context.Students.ProjectTo<StudentDto>(configuration: mapper.ConfigurationProvider).ToList();
مطالب
الگوی Chain Of Responsibility در #C
در این مطلب قصد داریم الگوی Chain Of Responsibility را تحت یک مثال کاربردی در زبان سی شارپ، با هم بررسی کنیم. اجازه دهید با یک مثال کار را شروع کنیم. سناریوی گرفتن وام دانشجویی را در نظر بگیرید؛ به این صورت که دانشجو وارد سامانه شده، رمز خود یا شماره دانشجویی خود را زده و درخواست خود را ثبت می‌کند و پاسخی را از سیستم دریافت میکند. فرض کنید سلسله مراتب سیستم به این صورت باشد که ابتدا بررسی میکند که دانشجو فعال باشد. مرحله بعد رمز دانشجو صحیح باشد. مرحله بعد اینکه مقدار وامی که قبلا گرفته است، از حداکثر وام ثبت شده در سیستم بیشتر نباشد و مرحله آخر هم ثبت درخواست وام. سناریوی ذکر شده صرفا جهت کار با الگوی مورد نظر در نظر گرفته شده است؛ چون قطعا روند کار بر پایه چارچوب طولانی‌تری پیش می‌رود.

اولین راه حلی که به ذهن میرسد
  1. if else
  2. switch case

بله مورد اولی که به ذهن خود من رسید، استفاده از if else هست. شاید خروجی مناسبی را از نظر کدنویسی داشته باشد؛ ولی خوانایی مناسبی را ندارد. حالا چطور اثبات کنیم خوانایی و قابلیت توسعه‌ی پایینی را دارد؟
فرض کنید شما برنامه را نوشته‌اید و تحویل مدیر خود داده‌اید. بعد از دو ماه به شما گفته می‌شود که مراحل 1 و 2 را جابجا کنید و یا یک step را اضافه کنید که بعد از مرحله دو (بررسی رمز) است تا یک منطق جدید را دنبال کند. اینجاست که دچار دردسر و اتلاف زمان میشویم؛ چون باید بیزینس را مجددا review کنیم و بدتر از آن کدها را هم تغییر دهیم که امکان رخ دادن خطا به شدت بالا می‌رود.

هدف از این الگو
  1. انجام کار در چند مرحله
  2. حذف پیچیدگی‌های پیاده سازی

حالا بیایید با هم با الگوی Chain Of Responsibility، این مثال را پیاده سازی کنیم. منطق کار به صورت زیر است:


به این شکل که مراحل بصورت سلسله مراتبی، تحت successor‌های یکدیگر پیش می‌روند. اگر بخواهم successor را در این مثال توضیح دهم من به‌عنوان دانشجو (successor اول) بعد از چک شدن موارد مربوط به دانشجو، درخواست به سمت مسئول مربوطه رفته (successor دوم ) و الی اخر.

پیاده سازی
ابتدا باید یک مدل را برای دانشجویان یا مشتریان بسازیم:
public class Customer
{
    public string Password { get; set; }
    public string Stno { get; set; }
    public int value { get; set; }
    public bool Active { get; set; }
}
همانطور که از دیاگرام مشخص است، ما یک requestContext لازم داریم که در سلسله مراتب بیزینس جابجا شده و منطق‌های ما بر روی این کلاس انجام میشود:
public class RequestContext
{
    public int VamValue { get; set; }
    public Customer student { get; set; }
}
که شامل یک مقدار وام (مقدار حداکثر وام درخواستی برای هر دانشجو) ،ضمن اینکه فرض کنید value در Customer، مقدار حداکثر وام در نظر گرفته شده‌ی در سیستم، برای دانشجو است. حال که ما یک درخواست را ایجاد میکنیم، باید یک کلاس response هم داشته باشیم:
public class ResponseContext
{
    public string Response { get; set; }
}

حال طبق شکل بالا باید handler خود را که گرفتن وام است، پیاده سازی نماییم:
public abstract class GetVam
{
    protected readonly GetVam successor;
    
    public GetVam(GetVam _getVam)
    {
        this.successor = _getVam;
    }

    public abstract ResponseContext execute(RequestContext requestContext);
}

حالا باید مراحل چندگانه‌‌ای را که عرض کردم، بصورت کلاس پیاده سازی نماییم:
1-چک کردن فعال بودن دانشجو :
public class CheckUseractive : GetVam
{
    public CheckUseractive(GetVam _getVam) : base(_getVam)
    {
    }

    public override ResponseContext execute(RequestContext requestContext)
    {
        if (requestContext.student.Active == true)
        {
            return successor.execute(requestContext);
        }

        else
        {

            return new ResponseContext
            {
                Response = "student is inactive"
            };
        }
    }
}

2-بررسی رمز کاربر :

public class ChechPassword : GetVam
{
    public ChechPassword(GetVam _getVam) : base(_getVam)
    {
    }

    public override ResponseContext execute(RequestContext requestContext)
    {
        if (requestContext.student.Password == "123")
        {
            return successor.execute(requestContext);
        }
        else
        {
            return new ResponseContext
            {
                Response = "invalid pass",
            };
        }
    }
}

3-بررسی میزان بدهکاری دانشجو :

public class ChechUserBedehkar : GetVam
{
    public ChechUserBedehkar(GetVam _getVam) : base(_getVam)
    {
    }

    public override ResponseContext execute(RequestContext requestContext)
    {
        if (requestContext.student.value < requestContext.VamValue)
        {
            return successor.execute(requestContext);
        }
        else
        {
            return new ResponseContext
            {
                Response = "you are dont permission"
            };
        }
    }
}

4-و مرحله آخر که در صورتیکه تمامی مراحل قبلی پاس شوند چک کردن مقدار وامی است که به دانشجو باید داده شود :

public class AssignVam : GetVam
{
    public AssignVam(GetVam _getVam) : base(_getVam)
    {
    }

    public override ResponseContext execute(RequestContext requestContext)
    {
        return new ResponseContext
        {
            Response = "value of vam: " + (requestContext.VamValue - requestContext.student.value).ToString();
        };
    }
}
که مابه التفاوت مقدار وام صندوق و مقدار وام گرفته شده دانشجو را به‌عنوان وام، به دانشجو برمی‌گردانیم.

تا اینجا ما منطق برنامه را نوشتیم حالا چطور از آن استفاده کنیم؟

partial class Program
{
    static void Main(string[] args)
    {
        Customer customer = new Customer()
        {
            Active = true,
            Password = "123",
            Stno = "111",
            value = 2000

        };

        RequestContext requestContext = new RequestContext()
        {

            student = customer,
            VamValue = 3000,
        };

        var GetVam = new CheckUseractive(new ChechPassword(new ChechUserBedehkar(new AssignVam(null))));
        var res = GetVam.execute(requestContext);
        Console.Write(res.Response);
        Console.ReadKey();
    }
}
خروجی:

حال اگر به نحوه فراخوانی دقت کنید، دقیقا سلسله مراتب، تحت کنترل ما است و در صورت تغییر و یا جابجایی stepهای برنامه، به سادگی قابل توسعه است.
نظرات اشتراک‌ها
کتابخانه tinymce
Required کردن TinyMCE با اتریبیوت اعتبارسنجی سفارشی
 سمت سرور :
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
public class MakeTinyMceRequiredAttribute : ValidationAttribute, IClientModelValidator
{
    public MakeTinyMceRequiredAttribute()
    {
        ErrorMessage = "لطفا {0} را وارد نمایید";
    }

    protected override ValidationResult IsValid(object value,
        ValidationContext validationContext)
    {
        var displayName = validationContext.DisplayName;
        ErrorMessage = ErrorMessage.Replace("{0}", displayName);

        if (string.IsNullOrWhiteSpace(value?.ToString()))
        {
            return new ValidationResult(ErrorMessage);
        }
        return ValidationResult.Success;
    }

    public void AddValidation(ClientModelValidationContext context)
    {
        var displayName = context.ModelMetadata.ContainerMetadata
            .ModelType.GetProperty(context.ModelMetadata.PropertyName)
            .GetCustomAttributes(typeof(DisplayAttribute), false)
            .Cast<DisplayAttribute>()
            .FirstOrDefault()?.Name;
        ErrorMessage = ErrorMessage.Replace("{0}", displayName);

        MergeAttribute(context.Attributes, "data-val", "true");
        MergeAttribute(context.Attributes, "data-val-makeTinyMceRequired", ErrorMessage);
    }
    public bool MergeAttribute(IDictionary<string, string> attributes, string key, string value)
    {
        if (attributes.ContainsKey(key))
        {
            return false;
        }
        attributes.Add(key, value);
        return true;
    }
}
سمت کلاینت :
if (jQuery.validator) {
    // For hidden inputs
    $.validator.setDefaults({
        ignore: []
    });

    // makeTinyMceRequired
    jQuery.validator.addMethod('makeTinyMceRequired', function (value, element, param) {
        var editorId = $(element).attr('id');
        var editorContent = tinyMCE.get(editorId).getContent();
        $('body').append(`<div id="test-makeTinyMceRequired">${editorContent}</div>`);
        var result = isNullOrWhitespace($('#test-makeTinyMceRequired').text());
        $('#test-makeTinyMceRequired').remove();
        return !result;
    });
    jQuery.validator.unobtrusive.adapters.addBool('makeTinyMceRequired');
}
function isNullOrWhitespace(input) {
    if (typeof input === 'undefined' || input == null)
        return true;
    return input.replace(/\s/g, '').length < 1;
}
نحوه انجام کار :
متنِ ادیتور TinyMCE داخل یک div موقت ریخته میشه بعد text اون div با isNullOrWhitespace بررسی میشه، اگه کاربر مقداری رو وارد کرده باشه اعتبارسنجی پاس میشه در غیر اینصورت متن خطارو نمایش میده.
نظرات مطالب
PersianDatePicker یک DatePicker شمسی به زبان JavaScript که از تاریخ سرور استفاده می‌کند
با فرض اینکه layout برنامه تعاریف لازم را دارد:
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <link href="~/Content/PersianDatePicker.css" rel="stylesheet" />
</head>
<body>
    @RenderBody()
    <script src="~/Scripts/jquery-1.10.2.min.js"></script>
    <script src="~/Scripts/PersianDatePicker.js"></script>
    @RenderSection("Scripts", required: false)
</body>
</html>
فایلی را به نام PersianDatePicker.cshtml در مسیر Views\Shared\EditorTemplates ایجاد کنید؛ با این محتوا:
@using System.Globalization
@model DateTime?
@{
    Func<DateTime, string> toPersianDate = date =>
    {
        var dateTime = new DateTime(date.Year, date.Month, date.Day, new GregorianCalendar());
        var persianCalendar = new PersianCalendar();
        return persianCalendar.GetYear(dateTime) + "/" + 
               persianCalendar.GetMonth(dateTime).ToString("00") + "/" + 
               persianCalendar.GetDayOfMonth(dateTime).ToString("00");
    };
    var today = toPersianDate(DateTime.Now);
    var name = this.ViewContext.ViewData.ModelMetadata.PropertyName;
    var value = Model.HasValue ? toPersianDate(Model.Value) : string.Empty;
}
<input type="text" dir="ltr" 
       name="@name" id="@name" 
       value="@value" 
       onclick="PersianDatePicker.Show(this,'@today');" />
بعد از این تنها کاری که باید انجام شود، استفاده از ویژگی UIHint است:
// Location: Views\Shared\EditorTemplates\PersianDatePicker.cshtml
[UIHint("PersianDatePicker")]
public DateTime AddDate { set; get; }
به صورت خودکار به متد EditorFor اعمال می‌شود:
<div>
    <label>تاریخ:</label>
    @Html.EditorFor(model => model.AddDate)
</div>

همچنین اگر نیاز است مقدار رشته تاریخ شمسی آن به یک DateTime میلادی انتساب داده شود (در حین ارسال اطلاعات به سرور)، باید از یک model binder سفارشی برای اینکار در ASP.NET MVC استفاده کنید. یک نمونه پیاده سازی شده آن‌را در بحث PersianDateModelBinder می‌توانید مشاهده و استفاده کنید.