نظرات مطالب
تاریخ شمسی با Extension Method برای DateTime
سلام خیلی خوبه منم از کلاس زیر استفاده می‌کنم
اما تو سیلورلایت چطور استفاده کنم؟

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text;


public class PersianCulture : CultureInfo
{
    private readonly Calendar cal;
    private readonly Calendar[] optionals;


    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[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", string.Empty };
        DateTimeFormat.MonthGenitiveNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", string.Empty };
        DateTimeFormat.AbbreviatedMonthNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", string.Empty };
        DateTimeFormat.AbbreviatedMonthGenitiveNames = new[] { "فروردین", "اردیبهشت", "خرداد", "تیر", "مرداد", "شهریور", "مهر", "آبان", "آذر", "دی", "بهمن", "اسفند", string.Empty };

        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; }
    }
}


نظرات اشتراک‌ها
معرفی کتابخانه DNTCommon.Web.Core
با سلام
 من ورژن  پکیج DNTCommon.Web.Core  به ورژن   2.7.0  آپدیت کردم و در اجرای پروژه به مشکل برخوردم 

Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: DNTCommon.Web.Core.IBackgroundQueueService Lifetime: Singleton ImplementationType: DNTCommon.Web.Core.BackgroundQueueService': Unable to resolve service for type 'DNTCommon.Web.Core.IJobsRunnerTimer' while attempting to activate 'DNTCommon.Web.Core.BackgroundQueueService'.)

اشتراک‌ها
َAngular 9.0.0-rc0 منتشر شد

New Breaking Changes

Angular now compiles with Ivy by default. See Ivy compatibility section.

Typescript 3.4 and 3.5 are no longer supported. Please update to Typescript 3.6.

tslib is now listed as a peer dependency rather than a direct dependency. Users not using the CLI will need to manually install tslib via yarn add tslib or npm install tslib --save. 

َAngular 9.0.0-rc0 منتشر شد
مطالب
هدایت خودکار آدرس‌های یافت نشد در یک سایت ASP.NET MVC به جستجوی سایت
هر از چندگاهی یک چنین آدرس‌های یافت نشدی را در لاگ‌های سایت مشاهده می‌کنم:
https://www.dntips.ir/jquery
https://www.dntips.ir/mvc
https://www.dntips.ir/برنامه
روش متداول مدیریت این نوع آدرس‌ها، هدایت خودکار به صفحه‌ی 404 است. اما شاید بهتر باشد بجای اینکار، کاربران به صورت خودکار به صفحه‌ی جستجوی سایت هدایت شوند. در ادامه مراحل اینکار را بررسی خواهیم کرد.

الف) ساختار کنترلر جستجوی سایت
فرض کنید جستجوی سایت در کنترلری به نام Search و توسط اکشن متد پیش فرضی با فرمت زیر مدیریت می‌شود:
   [ValidateInput(false)] //برنامه نویس‌ها نیاز دارند تگ‌ها را جستجو کنند
  public virtual ActionResult Index(string term)
  {

ب) مدیریت کنترلرهای یافت نشد
اگر از یک IoC Container در برنامه‌ی ASP.NET MVC خود مانند StructureMap استفاده می‌کنید، نوشتن کد متداول زیر کافی نیست:
public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
    }
از این جهت که اگر کاربر آدرس https://www.dntips.ir/test را وارد کند، controllerType درخواستی نال خواهد بود؛ چون جزو کنترلرهای سایت نیست. به همین جهت نیاز است موارد نال را هم مدیریت کرد:
public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
        {
            if (controllerType == null)
            {
                var url = requestContext.HttpContext.Request.RawUrl;
                //string.Format("Page not found: {0}", url).LogException();

                requestContext.RouteData.Values["controller"] = MVC.Search.Name;
                requestContext.RouteData.Values["action"] = MVC.Search.ActionNames.Index;
                requestContext.RouteData.Values["term"] = url.GetPostSlug().Replace("-", " ");
                return ObjectFactory.GetInstance(typeof(SearchController)) as Controller;
            }
            return ObjectFactory.GetInstance(controllerType) as Controller;
        }
    }
کاری که در اینجا انجام شده، هدایت خودکار کلیه کنترلرهای یافت نشد برنامه، به کنترلر Search است. اما در این بین نیاز است سه مورد را نیز اصلاح کرد. در RouteData.Values جاری، نام کنترلر باید به نام کنترلر Search تغییر کند. زیرا مقدار پیش فرض آن، همان عبارتی است که کاربر وارد کرده. همچنین باید مقدار action را نیز اصلاح کرد، چون اگر آدرس وارد شده برای مثال https://www.dntips.ir/mvc/test بود، مقدار پیش فرض action همان test می‌باشد. بنابراین صرف بازگشت وهله‌ای از SearchController تمام موارد را پوشش نمی‌دهد و نیاز است دقیقا جزئیات سیستم مسیریابی نیز اصلاح شوند. همچنین پارامتر term اکشن متد index را هم در اینجا می‌شود مقدار دهی کرد. برای مثال در اینجا عبارت وارد شده اندکی تمیز شده (مطابق روش متد تولید Slug) و سپس به عنوان مقدار term تنظیم می‌شود.

ج) مدیریت آدرس‌های یافت نشد پسوند دار
تنظیمات فوق کلیه آدرس‌های بدون پسوند را مدیریت می‌کند. اما اگر درخواست رسیده به شکل https://www.dntips.ir/mvc/test/file.aspx بود، خیر. در اینجا حداقل سه مرحله را باید جهت مدیریت و هدایت خودکار آن به صفحه‌ی جستجو انجام داد
- باید فایل‌های پسوند دار را وارد سیستم مسیریابی کرد:
 routes.RouteExistingFiles = true; //نیاز هست دانلود عمومی فایل‌ها تحت کنترل قرار گیرد
- در ادامه نیاز است مسیریابی Catch all اضافه شود:
پس از مسیریابی پیش فرض سایت (نه قبل از آن)، مسیریابی ذیل باید اضافه شود:
routes.MapRoute(
                "CatchAllRoute", // Route name
                "{*url}", // URL with parameters
                new { controller = "Search", action = "Index", term = UrlParameter.Optional, area = "" }, // Parameter defaults                
                new { term = new UrlConstraint() }
            );
مسیریابی پیش فرض، تمام آدرس‌های سازگار با ساختار MVC را می‌تواند مدیریت کند. فقط حالتی از آن عبور می‌کند که پسوند داشته باشد. با قرار دادن این مسیریابی جدید پس از آن، کلیه آدرس‌های مدیریت نشده به کنترلر Search و اکشن متد Index آن هدایت می‌شوند.
مشکل! نیاز است پارامتر term را به صورت پویا مقدار دهی کنیم. برای اینکار می‌توان یک RouteConstraint سفارشی نوشت:
    public class UrlConstraint : IRouteConstraint
    {
        public bool Match(System.Web.HttpContextBase httpContext, 
                          Route route, string parameterName, 
                          RouteValueDictionary values, 
                          RouteDirection routeDirection)
        {
            var url = httpContext.Request.RawUrl;
            //string.Format("Page not found: {0}", url).LogException();

            values["term"] = url.GetPostSlug().Replace("-", " ");
            return true;
        }
    }
UrlConstraint مطابق تنظیم CatchAllRoute فقط زمانی فراخوانی خواهد شد که برنامه به این مسیریابی خاص برسد (و نه در سایر حالات متداول کار با کنترلر جستجو). در اینجا فرصت خواهیم داشت تا مقدار term را به RouteValueDictionary آن اضافه کنیم.
مطالب
AOP با استفاده از Microsoft Unity
چند روز پیش فرصتی پیش آمد تا بتوانم مروری بر مطلب منتشر شده درباره AOP داشته باشم. به حق مطلب مورد نظر، بسیار خوب و مناسب شرح داده شده بود و همانند سایر مقالات جناب نصیری چیزی کم نداشت. اما امروز قصد پیاده سازی یک مثال AOP، با استفاده از Microsoft Unity Application Block را به عنوان IOC Container دارم. اگر شما هم، مانند من از UnityContainer به عنوان IOC Container در پروژه‌های خود استفاده می‌کنید نگران نباشید. این کتابخانه به خوبی از مباحث Interception پشتیبانی می‌کند. در ادامه طی یک مقاله این مورد را با هم بررسی می‌کنیم.
برای دوستانی که با AOP آشنایی ندارند پیشنهاد می‌شود ابتدا مطلب مورد نظر را یک بار مطالعه نمایند.
برای شروع یک پروژه در VS.Net بسازید و ارجاع به اسمبلی‌های زیر را در پروژه فراموش نکنید:
»Microsoft.Practices.EnterpriseLibrary.Common
»Microsoft.Practices.Unity
»Microsoft.Practices.Unity.Configuration
»Microsoft.Practices.Unity.Interception
»Microsoft.Practices.Unity.Interception.Configuration

یک اینترفیس به نام IMyOperation بسازید:
    public interface IMyOperation
    {      
        void DoIt();
    }

کلاسی می‌سازیم که اینترفیس بالا را پیاده سازی نماید:
 public void DoIt()
  {
     Console.WriteLine( "this is main block of code" );
  }
قصد داریم با استفاده از AOP یک سری کد مورد نظر خود(در این مثال کد لاگ کردن عملیات در یک فایل مد نظر است) را به کد‌های متد‌های مورد نظر تزریق کنیم. یعنی با فراخوانی این متد کد‌های لاگ عملیات در یک فایل ذخیره شود بدون تکرار یا فراخوانی دستی متد لاگ.
ابتدا یک کلاس برای لاگ عملیات می‌سازیم:
public class Logger
    {
        const string path = @"D:\Log.txt";

        public static void WriteToFile( string methodName )
        {
            object lockObject = new object();
            if ( !File.Exists( path ) )
            {
                File.Create( path );
            }
            lock ( lockObject )
            {
                using ( TextWriter writer = new StreamWriter( path , true ) )
                {
                    writer.WriteLine( string.Format( "{0} at {1}" , methodName , DateTime.Now ) );                
                }
            }
        }
    }
حال نیاز به یک Handler برای مدیریت فراخوانی کد‌های تزریق شده داریم. برای این کار یک کلاس می‌سازیم که اینترفیس ICallHandler را پیاده سازی نماید.
public class LogHandler : ICallHandler
    {
        public IMethodReturn Invoke( IMethodInvocation input , GetNextHandlerDelegate getNext )
        {
            Logger.WriteToFile( input.MethodBase.Name );

            var methodReturn = getNext()( input , getNext );         

            return methodReturn;
        }

        public int Order { get; set; }
    }
کلاس بالا یک متد به نام Invoke دارد که فراخوانی متد‌های مورد نظر برای تزریق کد را در دست خواهد گرفت. در این متد ابتدا عملیات لاگ در فایل مورد نظر ثبت می‌شود(با استفاده از Logger.WriteToFile). سپس با استفاده از getNext که از نوع GetNextHandlerDelegate است، اجرا را به کد‌های اصلی برنامه منتقل می‌کنیم.
 var methodReturn = getNext()( input , getNext );
برای مدیریت بهتر عملیات لاگ یک Attribute می‌سازیم که فقط متد هایی که نیاز به لاگ کردن دارند را مشخص کنیم. به صورت زیر:
 public class LogAttribute : HandlerAttribute
    {
        public override ICallHandler CreateHandler( Microsoft.Practices.Unity.IUnityContainer container )
        {
            return new LogHandler();
        }
    }
فقط دقت داشته باشید که کلاس مورد نظر به جای ارث بری از کلاس Attribute باید از کلاس HandlerAttribute که در فضای نام Microsoft.Practices.Unity.InterceptionExtension  تعبیه شده است ارث ببرد(خود این کلاس از کلاس Attribute ارث برده است).  کافیست در متد CreateHandler آن که Override شده است یک نمونه از کلاس LogHandler را برگشت دهیم.
برای آماده سازی Ms Unity جهت عملیات Interception باید کد‌های زیر در ابتدا برنامه قرار داده شود:
var  unityContainer = new UnityContainer();

 unityContainer.AddNewExtension<Interception>();

  unityContainer.Configure<Interception>().SetDefaultInterceptorFor<IMyOperation>( new InterfaceInterceptor() );
            
  unityContainer.RegisterType<IMyOperation, MyOperation>();

توضیح چند مطلب:
بعد از نمونه سازی از کلاس UnityContainer باید Interception به عنوان یک Extension به این Container اضافه شود. سپس با استفاده از متد Configure برای اینترفیس IMyOperation یک Interceptor پیش فرض تعیین می‌کنیم. در پایان هم به وسیله متد RegisterType کلاس MyOperation  به اینترفیس IMyOperation رجیستر می‌شود. از این پس هر گاه درخواستی برای اینترفیس IMyOperation از unityContainer شود یک نمونه از کلاس MyOperation در اختیار خواهیم داشت.
به عنوان نکته آخر متد DoIt در اینترفیس بالا باید دارای LogAttribute باشد تا عملیات مزین سازی با کد‌های لاگ به درستی انجام شود.

یک نکته تکمیلی:
در هنگام مزین سازی متد  set خاصیت ها، به دلیل اینکه اینترفیسی برای این کار وجود ندارد باید مستقیما عملیات AOP به خود کلاس اعمال شود. برای این کار باید به صورت زیر عمل نمود:

var container = new UnityContainer();
container.RegisterType<Book , Book>();

container.AddNewExtension<Interception>();

 var policy = container.Configure<Interception>().SetDefaultInterceptorFor<Book>( new VirtualMethodInterceptor() ).AddPolicy( "MyPolicy" );

  policy.AddMatchingRule( new PropertyMatchingRule( "*" , PropertyMatchingOption.Set ) );
  policy.AddCallHandler<Handler.NotifyChangedHandler>();
همان طور که مشاهده می‌کنید عملیات Interception مستقیما برای کلاس پیکر بندی می‌شود و به جای InterfaceInterceptor از VirtualMethodInterceptor برای تزریق کد به بدنه متد‌ها استفاده شده است. در پایان نیز با تعریف یک Policy می‌توانیم به راحتی(با استفاده از "*") متد Set  تمام خواص کلاس را به NotifyChangedHandler مزین نماییم.

سورس کامل مثال بالا
مطالب
استفاده از پروایدر SQLite در Entity Framework 7
Entity Framework در نگارش 7 خود از منابع داده‌ایی جدیدی پشتیبانی میکند(+) . یعنی از Windows Phone، Windows Store و همچنین ASP.NET 5 (اپلیکیشن‌هایی که از NET Core. استفاده می‌کنند) پشتیبانی خواهد کرد. در این نسخه از دیتابیس‌های non-relational نیز پشتیبانی می‌شود. پروایدر SQLite به صورت رسمی توسط تیم EF ارائه شده است که در ادامه نحوه‌ی استفاده از آن را در یک برنامه کنسول ساده بررسی خواهیم کرد.
کلاس‌های برنامه:
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
using System.Collections.Generic;
using System.Linq;

namespace UsingEF7WithSQLite
{
    public class Blog
    {
        public int BlogId { get; set; }
        public string Url { get; set; }

        public List<Post> Posts { get; set; }
    }

    public class Post
    {
        public int PostId { get; set; }
        public string Title { get; set; }
        public string Content { get; set; }

        public int BlogId { get; set; }
        public Blog Blog { get; set; }
    }
}
خب تا اینجا مدل‌های برنامه را تعریف کردیم، قدم بعدی افزودن پکیج مربوط به پروایدر SQLite به پروژه است، با دستور زیر این پکیج را نصب می‌کنیم:
PM> Install-Package EntityFramework.SQLite –Pre
اکنون کلاس کانتکست برنامه را به صورت زیر تعریف می‌کنیم:
namespace UsingEF7SQLiteProvider
{
    public class BloggingContext : DbContext
    {
        public DbSet<Blog> Blogs { get; set; }
        public DbSet<Post> Posts { get; set; }

        protected override void OnConfiguring(DbContextOptions builder)
        {
            builder.UseSQLite(@"Data Source=.\BloggingDatabae.db");
        }

        protected override void OnModelCreating(ModelBuilder builder)
        {
            builder.Entity<Blog>()
                .OneToMany(b => b.Posts, p => p.Blog)
                .ForeignKey(p => p.BlogId);

            // The EF7 SQLite provider currently doesn't support generated values
            // so setting the keys to be generated from developer code
            builder.Entity<Blog>()
                .Property(b => b.BlogId)
                .GenerateValueOnAdd(false);

            builder.Entity<Post>()
                .Property(b => b.BlogId)
                .GenerateValueOnAdd(false);
        }
    }
}

کار را با بازنویسی متد OnConfiguration شروع می‌کنیم، در این قسمت باید به EF بگوئیم که می‌خواهیم از SQLite استفاده کنیم برای اینکار از یک Extension Method با نام UseSQLite و پاس دادن کانکشتن استرینگ به آن استفاده می‌کنیم.
نکته: پروایدر فعلی SQLite در حال حاضر از Generated values پشتیبانی نمی‌کند، برای این منظور باید درون متد OnModelCreating این قابلیت را غیرفعال کنیم.
اکنون می‌توانیم از طریق پاورشل نیوگت دیتابیس را ایجاد کنیم، برای اینکار باید پکیج زیر را به پروژه اضافه کنید:
Install-Package EntityFramework.Commands -Pre
سپس دستورات زیر را اجرا می‌کنیم:
Add-Migration MyFirstMigration
Apply-Migration
توسط دستور Apply-Migrate دیتابیس برای شما ایجاد خواهد شد. البته این دستور زمانی استفاده می‌شود که برنامه شما یک اپلیکیشن دسکتاپ باشد. اگر اپلیکیشن شما یک Windows Phone Application است باید در زمان اجرای برنامه این کد را بنویسید:
using (var db = new BloggingContext())
{
   db.Database.AsMigrationsEnabled().ApplyMigrations();
}
در نهایت می‌توانید با دستور زیر از کانتکست برنامه استفاده کرده و خروجی را مشاهده کنید:
using (var db = new Models.BloggingContext())
{
     db.Blogs.Add(new Models.Blog { Url = "https://www.dntips.ir" });
     db.SaveChanges();

     foreach (var item in db.Blogs)
     {
         Console.WriteLine(item.Url);
     }
}
Console.ReadLine();
مطالب
بخش سوم - بررسی امکانات و کدنویسی در کامپایلر Svelte
در بخش‌های قبل تا حدودی با کامپایلر Svelte و ساختار فایل‌های آن آشنا شدیم. در این بخش با چند مثال قصد دارم امکاناتی را که Svelte در اختیارمان قرار میدهد، شرح دهم. 

Dynamic attributes : 
در بخش قبل دیدیم که با استفاده از علامت ( آکولاد {} ) میتوانیم مقادیر موجود در تگ اسکریپت را در html خود رندر کنیم. ولی از این علامت میتوان برای مقدار دهی attribute‌ها هم استفاده کرد. 
<script>
let src = 'https://svelte.dev/tutorial/image.gif';
let name = 'Rick Astley';
</script>

<img src={src} alt="{name} dancing">
اگر این کد را در بدنه کامپوننت خود یعنی همان فایل App.svelte قرار دهیم، به درستی کار خواهد کرد و مقدار‌های متغیر‌های src و name، در تگ img ما قرار خواهند گرفت. چند نکته در اینجا وجود دارد که بهتر است به آنها اشاره کنم.

  • نکته اول : اگر در تگ img مقدار alt را وارد نکنیم و یا alt در این تگ وجود نداشته باشد، یک هشدار توسط کامپایلر svelte برای ما با عنوان <img> element should have an alt attribute> ایجاد میشود. زمان ساخت یک برنامه بسیار مهم است تا قوانین نوشتن یک کد html خوب را رعایت کنیم تا برای تمامی کاربران احتمالی برنامه قابل استفاده باشد. در همین مثال با ایجاد یک هشدار Svelte تلاش میکند که ما را از اشتباه در نوشتن کد html مطلع سازد.

  • نکته دوم : اگر نام یک آبجکت تعریف شده و یک attribute، برابر باشد میتوانیم از نسخه کوتاه شده یا Shorthand attributes در svelte استفاده کنیم. به طور مثال در مثال بالا میتوانیم از کد زیر در خط 6 استفاده کنیم.
<img {src} alt="{name} dancing">


Nested components :
همانطور که قبلا اشاره کرده بودم، همه‌چیز در svelte از کامپوننت‌ها تشکیل میشود. در یک برنامه واقعی درست نیست که تمامی کد برنامه را مستقیما در کامپوننت اولیه برنامه بنویسیم. به جای آن بهتر است هر بخش از وبسایت، کامپوننت مجزایی داشته باشد تا کدها و استایل‌ها و html ما نظم و خوانایی بیشتری پیدا کنند.
یک فایل دیگر را در کنار کامپوننت App.svelte که در بخش قبلی ایجاد کرده بودیم، به نام Nested.svelte ایجاد کنید و در آن کد زیر را قرار دهید.
<script>
  export let siteName = "dotnettips";
</script>

<p>this is a nested component for third tutorial on {siteName}</p>
در این مرحله ما یک کامپوننت جدید را ایجاد کردیم که برای نمایش آن باید به App.svelte اضافه شود. برای انجام اینکار محتوای App.svelte را به کد زیر تغییر دهید.
<script>
  import Nested from "./Nested.svelte";

  export let name;
</script>

<h1>Hello {name}!</h1>

<Nested siteName="dotnettips.info" />
که در نهایت چنین خروجی خواهیم داشت. 
 Hello world!

this is a nested component for third tutorial on dotnettips.info


در مثال بالا ما یک کامپوننت جدید را ایجاد کرده و از طریق دستور import به App.svelte اضافه کردیم. نکته‌ای که در اینجا وجود دارد، نحوه مقدار دهی props در کامپوننت‌ها است. اگر به خط 9 دقت کنیم، کامپوننت ما از طریق تگ جدیدی با نام (Nested) به بدنه html برنامه اضافه شده است که یک attribute به نام siteName دارد. siteName متغیر export شده در کامپوننت Nested.svelte است که در کامپوننت‌ها به این صورت مقدار دهی میشود. قبلا نحوه مقدار دهی این خصیصه‌ها را در فایل‌های جاوا اسکریپت مشاهده کرده بودیم. نکته دیگری که باید به آن دقت داشت این است که خصیصه siteName مقدار پیش فرض dotnettips را در Nested.svelte به خود اختصاص داده بود. به همین جهت اگر ما siteName را هنگام استفاده از کامپوننت مقدار دهی نکنیم، از مقدار پیش فرض خود استفاده خواهد کرد. ولی اینجا ما با مقدار دهی آن، siteName را به dotnettips.info تغییر داده‌ایم.

نکته مهم : دقت داشته باشید کامپوننت‌های شما همیشه باید با حروف بزرگ شروع شوند؛ به طور مثال در صورت نوشتن <nested/> محتوای کامپوننت نمایش داده نخواهد شد. svelte، از طریق زیر نظر گرفتن حروف کوچک و بزرگ در ابتدای تگ‌ها، بین تگ‌های html و کامپوننت‌ها تمایز قائل میشود.



Spread  props :

تا اینجا به صورت خلاصه با props یا خصیصه‌ها آشنا شده‌اید و دیدیم که با export کردن یک متغیر در یک کامپوننت، میتوانیم آن را هنگام استفاده مقدار دهی نماییم. برای اینکه تمرینی هم باشد با توجه به مطالبی که تاکنون گفته شده، پروژه‌ی جدیدی را ایجاد کنید و محتوای App.svelte را مانند کد زیر تغییر دهید.

<script>
import Info from './Info.svelte';

const pkg = {
name: 'svelte',
version: 3,
speed: 'blazing',
website: 'https://svelte.dev'
};
</script>

<Info name={pkg.name} version={pkg.version} speed={pkg.speed} website={pkg.website}/>

همانطور که در خط دوم کد می‌بینید، کامپوننتی به نام Info.svelte به این بخش اضافه شده‌است. این کامپوننت را با محتوای زیر ایجاد نمایید:

<script>
export let name;
export let version;
export let speed;
export let website;
</script>

<p>
The <code>{name}</code> package is {speed} fast.
Download version {version} from <a href="https://www.npmjs.com/package/{name}">npm</a>
and <a href={website}>learn more here</a>
</p>

اگر برنامه را اجرا کنید یک چنین خروجی را مشاهده خواهید کرد: 

 The svelte  package is  blazing  fast. Download version  3  from  npm  and  learn more here
در این مثال در کامپوننت info.svelte حدودا تعداد زیادی متغیر export شده داریم که دقیقا همنام با آنها در App.svelte یک آبجکت به نام pkg مقدار دهی شده‌است (خط 4-9). در این شرایط نیازی به نوشتن تک تک خصیصه‌ها مانند کاری که در خط 12 کردیم نیست. خیلی ساده میتوانیم از امکانات ES6 برای destruct کردن این آبجکت و ست کردن این خصیصه‌ها استفاده کنیم؛ به این صورت : 
<Info {...pkg}/>



مروری کوتاه بر مدیریت Event ها  در svelte:
قدرت اصلی svelte، واکنش پذیر بودن آن است. به این معنی که همیشه DOM را با وضعیت یا state برنامه هماهنگ نگه میدارد. یکی از مواردی که بطور مثال این امکان را به خوبی نمایش میدهد، event‌ها هستند. 
به مثال زیر توجه کنید:
<script>
  let count = 0;

  function handleClick() {
    count += 1;
  }
</script>

<p>Count : {count}</p>
<button on:click={handleClick}>
  Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
در مثال بالا در خط 10، یک button را ایجاد کردیم که به متد click آن یک function اختصاص داده شده که در خط 4 تعریف کرده بودیم. برای bind کردن یک event در svelte از on: استفاده میکنیم. البته دقت کنید که در خط 10، نام function بدون پرانتز نوشته شده‌است؛ چرا که ما قصد اجرای آن را نداریم و صرفا میخواهیم این فانکشن پس از کلیک شدن بر روی button اجرا شود. در خط 5 هم در بدنه فانکشن خود که پس از هر کلیک اجرا خواهد شد، یک واحد به متغیر count اضافه میکنیم. در خطوط 9 و 11 هم مقدار count را نمایش داده‌ایم. 
در بخش‌های بعد به جزئیات کار با event‌ها بیشتر میپردازیم.

واکنش پذیری یا Reactivity در svelte :
svelte به صورت خودکار DOM را پس از اینکه وضعیت کامپوننت یا آبجکت‌های شما تغییر کند، آپدیت میکند؛ ولی در این بین چندین حالت وجود دارند که به صورت خودکار svelte از تغییر وضعیت کامپوننت شما آگاه نمیشود. 
عملگر :$
به مثال زیر توجه کنید. 
<script>
let count = 0;
let doubled = count * 2;

function handleClick() {
count += 1;
}
</script>

<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>

<p>{count} doubled is {doubled}</p>
در این مثال در خط سوم متغیر doubled تعریف شده که قرار است دو برابر count را در خود نگه دارد. اگر این کد را اجرا نمایید، متوجه خواهید شد که مقدار double پس از تغییر count، تغییری نخواهد کرد. چرا که بر خلاف سایر فریم ورک‌ها، مانند react برای بهبود performance برنامه، svelte از Virtual-DOM استفاده نمیکند و این امر سبب میشود که کل کدهای کامپوننت ما در هر بار تغییر، در وضعیت بخشی از آن، مجددا اجرا نشود و مستقیما بخش مورد نظر در DOM تغییر کند. این امر سبب شده از نظر performance در کامپوننت‌های پیچیده که به طور مثال از انیمیشن یا visualisation‌ها استفاده میکنند، کامپوننت‌های svelte چندین برابر پرسرعت‌تر از سایر فریم ورک‌های مشابه عمل کند. ولی اگر بخواهیم که مقدار doubled آپدیت شود چه کاری باید کرد؟ 
راهکار اول مقدار دهی doubled در function مرتبط است، چرا که svelte همیشه به عملگر مساوی = در کد جاوا اسکریپت نگاه میکند. ولی این راهکار در مثال بالا منطقی به نظر نمی‌آید چرا که وظیفه متد ما صرفا دو برابر کردن مقدار count است.
راهکار دوم استفاده از عملگر :$  قبل از تعریف متغیر میباشد که به نظر راهکار مناسب‌تری است. پس در کد بالا فقط لازم است خط سوم را به کد زیر تغییر دهیم تا برنامه بدرستی مقدار doubled را پس از تغییر count محاسبه کند. 
$: doubled = count * 2;
به این طریق میتوانیم متغیر خود را وادار کنیم درصورت تغییر مقادیر، در سمت راست مساوی، مقدار doubled را مجددا محاسبه نماید.

استفاده از این عملگر در svelte به تعریف متغیر‌ها محدود نمیشود. ما هر جائیکه نیاز به واکنش پذیری نسبت به کامپوننت‌ها یا متغیر‌های خود داشتیم، میتوانیم از این عملگر استفاده کنیم. به طور مثال برای لاگ کردن اطلاعات، هر بار که مقدار count تغییر کرد میتوانیم از کد زیر استفاده کنیم. 
$: console.log(`the count is ${count}`);
همچنین میتوانیم به آسانی گروهی از فعالیت‌ها را با هم واکنش پذیر کنیم. مثال :
$: {
  console.log(`the count is ${count}`);
  alert(`I SAID THE COUNT IS ${count}`);
}
حتی میتوان از این عملگر قبل از بلاک‌های کد، مانند if یا for استفاده کرد. مثال:
$: if (count >= 10) {
     alert(`count is dangerously high!`);
     count = 9;
}

واکنش پذیری در آرایه‌ها و آبجکت‌ها : 
به مثال زیر توجه کنید:
<script>
  let numbers = [1, 2, 3, 4];

  function addNumber() {
    let newNumber = numbers.length + 1;
    numbers.push(newNumber);
  }

  $: sum = numbers.reduce((t, n) => t + n, 0);
</script>

<p>{numbers.join(' + ')} = {sum}</p>

<button on:click={addNumber}>Add a number</button>

در مثال بالا پس از هر کلیک، یک عدد به آرایه numbers اضافه میشود؛ ولی در خط 11 که این آرایه را نمایش میدهیم، مقدار آن تغییر نخواهد کرد! همانطور که قبلا اشاره کرده بودم svelte برای اینکه متوجه شود تغییری در آبجکت‌های ما صورت گرفته است، به مشاهده عملگر مساوی در کدهای ما میپردازد و چون اینجا در خط 6، ما از متد push آرایه برای افزودن مقداری جدید به آن درحال استفاده هستیم svelte واکنشی به این تغییر ندارد. برای رفع این مشکل دو راهکار وجود دارد. 
راهکار اول استفاده از عملگر مساوی و reassign کردن مقدار آبجکت یا متغیر مورد نظر است به این صورت : 
  function addNumber() {
    let newNumber = numbers.length + 1;
    numbers.push(newNumber);
    numbers = numbers;
  }
راهکار دوم استفاده از امکانات ES6 جاوا اسکریپت است برای افزودن مقدار جدید به آرایه که چون در این روش از مساوی استفاده میکنیم، تغییرات در numbers واکنش پذیر خواهد بود. به این صورت : 
  function addNumber() {
    let newNumber = numbers.length + 1;
    numbers = [...numbers, newNumber];
  }

مروری بر Two way bindings :
فرض کنید قصد داریم که یک input برای دریافت نام در صفحه قرار داده و در متغیری به نام name ذخیره کنیم. سپس این متغیر را در صفحه نشان دهیم. برای انجام اینکار حدودا با توجه به مطالبی که تا الان آموخته‌ایم میتوان کد زیر را تولید کرد : 
<script>
  let name = "";
  function updateName(event) {
    name = event.target.value;
  }
</script>

<h4>My Name Is {name}</h4>
<input value={name} on:input={updateName} />
در خط 9 یک input به کامپوننت خود اضافه کردیم و مقدار value آن را به متغیر name نسبت دادیم. تا اینجا فقط اگر name تغییری کند آن را در input میتوانیم نمایش دهیم ولی پس از تایپ در این input به صورت خودکار مقدار name آپدیت نمیشود چون ما در این حالت از One way binding درحال استفاده هستیم. به همین جهت یک متد برای آپدیت مقدار name توسط input هم باید مینوشتیم که اگر دقت کنید در کد فوق از ایونت input تگ input در خط 11 به همین منظور استفاده شده‌است. در خط سوم اینبار متد ما یک پارامتر هم به نام event دارد. به طور پیش فرض میتوانیم همیشه از طریق این پارامتر به ایونت آبجکت مبداء که فانکشن را صدا زده است، دسترسی داشته باشیم که خیلی ساده از طریق این ایونت در خط 4 مقدار name را پس تغییر در input آپدیت میکنیم. 
شاید کد بالا کاری که خواستیم را انجام داده باشد. ولی پیاده سازی Two way binding به شکلی که نوشتیم ساده نیست و مشکلات خاص خودش را دارد. خبر خوب این است که svelte این امکان را به صورت توکار محیا کرده است. 
برای اتصال یک attribute به یک متغیر یا آبجکت به صورت دو طرفه از کلمه :bind قبل از نام attribute در svelte استفاده میشود. مثال بالا را اگر با استفاده از این توضیح بازنویسی کنیم، به این کد خواهیم رسید:
<script>
  let name = "";
</script>

<h4>My Name Is {name}</h4>
<input bind:value={name} />
همانطور که مشاهده میکنید دیگر نیازی به نوشتن یک فانکشن مجزا و گرفتن اطلاعات از طریق پارامتر ایونت نیست و به سادگی مقدار value به متغیر name به صورت دو طرفه متصل شده است.


expressing logic :
html امکانی برای پیاده سازی منطق برنامه، برخلاف svelte ندارد. در ادامه با syntax پیاده سازی منطق برنامه در کنار کدهای html در svelte آشنا میشویم. 

If blocks
برای نوشتن If در svelte از if# در میان دو آکولاد {} استفاده میشود و برای پایان دادن به آن از if/ در میاد دو آکولاد. به این صورت : 
{#if condition}
    <!-- you html codes ...  -->
{/if}
به مثال زیر توجه کنید : 
در این مثال قصد داریم پس از کلیک بر روی کلید لاگین، وضعیت کاربر را لاگین شده قرار دهیم و کلید لاگین را مخفی کنیم. سپس کلید دیگری برای خروج از برنامه به نام logout را نمایش دهیم که با کلیک بر روی آن کاربر logout شود و مجددا کلید لاگین نمایش داده شود. برای شبیه سازی این سناریو از کد زیر میتوانیم استفاده کنیم :
<script>
let user = { loggedIn: false };

function toggle() {
user.loggedIn = !user.loggedIn;
}
</script>

{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{/if}

{#if !user.loggedIn}
<button on:click={toggle}>
Log in
</button>
{/if}
کدهای داخل اسکریپت که چندان نیازی به توضیح ندارند و صرفا برای toggle کردن وضعیت کاربر هستند. ولی در خطوط 9 - 13 و 15 - 19 طریقه نوشتن if در svelte را با یک مثال واقعی مشاهده میکنید.
در مثال بالا دو شرط با یک مقدار مشابه true/false نوشته شده است که بهتر بود مانند سایر زبان‌های برنامه نویسی از else در اینجا استفاده میشد. در ادامه با نحوه نوشتن else در svelte آشنا خواهیم شد.
Else blocks
برای نوشتن else در svelte از else: در میاد دو آکولاد {} بین یک block مانند if میتوانیم استفاده کنیم. به این صورت :
{#if condition}
    <!-- you html code when condition is true -->
{:else}
    <!-- you html code when condition is false -->
{/if}
باز نویسی دو if block در مثال بالا با استفاده از else : 
{#if user.loggedIn}
<button on:click={toggle}>
Log out
</button>
{:else}
<button on:click={toggle}>
Log in
</button>
{/if}

همینطور شما میتوانید از else if هم استفاده نمایید. به این صورت : 
{#if condition}
    <!-- you html code when condition is true -->
{:else if condition2}
    <!-- you html code when condition2 is true -->
{:else}
    <!-- you html code when condition and condition2 are false -->
{/if}

نکته تکمیلی : در svelte علامت # همیشه به معنای شروع یک بلاک کد است و علامت / به معنای پایان بلاک. همینطور علامت : نشان دهنده ادامه بلاک کد است. شاید در ابتدا کمی گیج کننده به نظر بیاید ولی در مثال‌های بعدی بیشتر با این علائم آشنا میشویم.

each blocks : 
برای حرکت در میان لیستی از اطلاعات در svelte میتوانیم از each# استفاده نماییم و مانند بلاک if برای خاتمه دادن به آن از each/ استفاده میشود. همینطور از کلمه کلیدی as هم برای دسترسی به هر آیتم در لیست استفاده میشود. به این صورت : 
{#each list as item}
      <!-- you html code per each item in list -->
{/each}
به مثال زیر توجه کنید : 
<script>
let cats = [
{ id: 'J---aiyznGQ', name: 'Keyboard Cat' },
{ id: 'z_AbfPXTKms', name: 'Maru' },
{ id: 'OUtn3pvWmpg', name: 'Henri The Existential Cat' }
];
</script>

<h1>The Famous Cats of YouTube</h1>

<ul>
{#each cats as cat}
<li><a target="_blank" href="https://www.youtube.com/watch?v={cat.id}">
{cat.name}
</a></li>
{/each}
</ul>
در خط 2 ما یک آرایه از گربه‌ها را تعریف کرده ایم و در خط 11 با استفاده از each به ازای هر کدام از آیتم‌های داخل این آرایه یک تگ li ایجاد میکنیم که لینک مرتبط با آن آیتم را نمایش میدهد. البته کد فوق را به شکل دیگری با استفاده از امکانات destructing جاوا اسکریپت هم میتوانستیم بنویسیم. به این صورت : 
<ul>
{#each cats as {id,name}}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{name}
</a></li>
{/each}
</ul>
در این حالت ابتدا id و name به صورت مجزا destruct شده اند و میتوانیم مانند مثال بالا از آنها بدون نوشتن نام آیتم استفاده کنیم . مانند خط 4 کد بالا.
همینطور در each بلاک‌ها امکان دریافت ایندکس جاری آیتم از طریق آرگومان دوم هم وجود دارد. به این صورت : 
<ul>
{#each cats as { id, name }, i}
<li><a target="_blank" href="https://www.youtube.com/watch?v={id}">
{i + 1}: {name}
</a></li>
{/each}
</ul>
اگر به کد فوق دقت کنید درخط 2 مقدار i ایندکس آیتم جاری آرایه ما یعنی همان cats است.


نکته : در این بخش من سعی کردم تا حدودی به ترتیب بخش آموزشی خود وبسایت Svelte، موارد را بیان کنم؛ ولی با توجه به اینکه شاید دوستان ترجیح بدهند روش آموزشی خود  آن وبسایت که امکان تغییر و نوشتن کد را هم محیا کرده است، امتحان کنند  لینک آن  را به اشتراک میگذارم. 
مطالب
MVC Scaffolding #3
شاید کیفیت کدهای تولیدی یا کدهای View حاصل از MVC Scaffolding مورد تائید شما نباشد. در این قسمت به نحوه تغییر و سفارشی سازی این موارد خواهیم پرداخت.

آشنایی با ساختار اصلی MVC Scaffolding

پس از نصب MVC Scaffolding از طریق NuGet به پوشه Packages مراجعه نمائید. در اینجا پوشه‌های MvcScaffolding، T4Scaffolding و T4Scaffolding.Core ساختار اصلی این بسته را تشکیل می‌دهند. برای نمونه اگر پوشه T4Scaffolding\tools را باز کنیم، شاهد تعدادی فایل ps1 خواهیم بود که همان فایل‌های پاورشل هستند. مطابق طراحی NuGet، همواره فایلی با نام init.ps1 در ابتدا اجرا خواهد شد. همچنین در  اینجا پوشه‌های T4Scaffolding\tools\EFRepository و T4Scaffolding\tools\EFDbContext نیز قرار دارند که حاوی قالب‌های اولیه کدهای مرتبط با الگوی مخزن و DbContext تولیدی می‌باشند.
در پوشه MvcScaffolding\tools، ساختار قالب‌های پیش فرض تولید Viewها و کنترلرهای تولیدی قرار دارند. در اینجا به ازای هر مورد، دو نگارش vb و cs قابل مشاهده است.


سفارشی سازی قالب‌های پیش فرض Viewهای MVC Scaffolding

برای سفارشی سازی قالب‌های پیش فرض از دستور کلی زیر استفاده می‌شود:
Scaffold CustomTemplate Name Template
مانند دستور زیر:
Scaffold CustomTemplate View Index
در اینجا View نام یک Scaffolder است و Index نام قالبی در آن.
اگر دستور فوق را اجرا کنیم، فایل جدیدی به نام CodeTemplates\Scaffolders\MvcScaffolding.RazorView\Index.cs.t4 به پروژه جاری اضافه می‌شود. از این پس کلیه فرامین اجرایی، از نسخه محلی فوق بجای نمونه‌های پیش فرض استفاده خواهند کرد.
در ادامه قصد داریم اندکی این قالب پیش فرض را جهت اعمال ویژگی DisplayName به هدر جدول تولیدی نمایش اطلاعات Tasks تغییر دهیم. در کلاس Task، خاصیت زمان موعود با ویژگی DisplayName مزین شده است. این نام نمایشی حین تولید فرم‌های ثبت و ویرایش اطلاعات بکار گرفته می‌شود، اما در زمان تولید جدول اطلاعات ثبت شده، به هدر جدول اعمال نمی‌گردد.
[DisplayName("Due Date")]
public DateTime? DueDate { set; get; }
برای تغییر و بهبود این مساله، فایل Index.cs.t4 را که پیشتر به پروژه اضافه کردیم باز کنید. کلاس ModelProperty را یافته و خاصیت جدید DisplayName را به آن اضافه کنید:
// Describes the information about a property on the model
class ModelProperty {
    public string Name { get; set; }
    public string DisplayName { get; set; }
    public string ValueExpression { get; set; }
    public EnvDTE.CodeTypeRef Type { get; set; }
    public bool IsPrimaryKey { get; set; }
    public bool IsForeignKey { get; set; }
    public bool IsReadOnly { get; set; }
}
در حالت پیش فرض فقط از خاصیت Name برای تولید هدر جدول در ابتدای فایل t4 در حال ویرایش استفاده می‌شود. در پایان فایل t4 جاری، متد زیر را اضافه کنید:
static string GetDisplayName(EnvDTE.CodeProperty prop)
{
  var displayAttr = prop.Attributes.OfType<EnvDTE80.CodeAttribute2>().Where(x => x.FullName == typeof(System.ComponentModel.DisplayNameAttribute).FullName).FirstOrDefault();
  if(displayAttr == null)
  {
    return prop.Name;
  }
  return displayAttr.Value.Replace("\"","");
}
در اینجا بررسی می‌شود که آیا ویژگی DisplayNameAttribute بر روی خاصیت در حال بررسی وجود دارد یا خیر. اگر خیر از نام خاصیت استفاده خواهد شد و اگر بلی، مقدار ویژگی نام نمایشی استخراج شده و بازگشت داده می‌شود.
اکنون برای اعمال متد GetDisplayName، متد GetEligibleProperties را یافته و به نحو زیر تغییر دهید:
results.Add(new ModelProperty {
Name = prop.Name,
DisplayName = GetDisplayName(prop), 
ValueExpression = "Model." + prop.Name,
Type = prop.Type,
IsPrimaryKey = Model.PrimaryKeyName == prop.Name,
IsForeignKey = ParentRelations.Any(x => x.RelationProperty == prop),
IsReadOnly = !prop.IsWriteable()
});
در اینجا خاصیت DisplayName به لیست خروجی اضافه شده است.
اکنون قسمت هدر جدول تولیدی را در ابتدای فایل t4 یافته و به نحو زیر تغییر می‌دهیم تا از DisplayName استفاده کند:
<#
List<ModelProperty> properties = GetModelProperties(Model.ViewDataType, true);
foreach (ModelProperty property in properties) {
    if (!property.IsPrimaryKey && !property.IsForeignKey) {
#>
        <th>
            <#= property.DisplayName #>
        </th>
<#
    }
}
#>
در ادامه برای آزمایش تغییرات فوق، دستور ذیل را صادر می‌کنیم:
PM> Scaffold Controller -ModelType Task -ControllerName TasksController -DbContextType TasksDbContext -Repository -Force
پس از اجرای دستور، به فایل Views\Tasks\Index.cshtml مراجعه نمائید. اینبار هدر خودکار تولیدی از Due Date بجای DueDate استفاده کرده است.


سفارشی سازی قالب‌های پیش فرض کنترلرهای MVC Scaffolding

در ادامه قصد داریم کدهای الگوی مخزن تهیه شده را اندکی تغییر دهیم. برای مثال با توجه به اینکه از تزریق وابستگی‌ها استفاده خواهیم کرد، نیازی به سازنده اولیه پیش فرض کنترلر که در بالای آن ذکر شده «در صورت استفاده از یک DI این مورد را حذف کنید»، نداریم. برای این منظور دستور زیر را اجرا کنید:
 PM> Scaffold CustomTemplate Controller ControllerWithRepository
در اینجا قصد ویرایش قالب پیش فرض کنترلرهای تشکیل شده با استفاده از الگوی مخزن را داریم. نام ControllerWithRepository از فایل ControllerWithRepository.cs.t4 موجود در پوشه packages\MvcScaffolding\tools\Controller گرفته شده است.
به این ترتیب فایل جدید CodeTemplates\Scaffolders\MvcScaffolding.Controller\ControllerWithRepository.cs.t4 به پروژه جاری اضافه خواهد شد. در این فایل چند سطر ذیل را یافته و سپس حذف کنید:
// If you are using Dependency Injection, you can delete the following constructor
        public <#= Model.ControllerName #>() : this(<#= String.Join(", ", Repositories.Values.Select(x => "new " + x.RepositoryTypeName + "()")) #>)
        {
        }
برای آزمایش آن دستور زیر را صادر نمائید:
PM> Scaffold Controller -ModelType Task -ControllerName TasksController -DbContextType TasksDbContext -Repository -Force -ForceMode ControllerOnly
چون تنها قصد تغییر کنترلر را داریم از پارامتر ForceMode با مقدار ControllerOnly استفاده شده است.
یا اگر نیاز به تغییر کدهای الگوی مخزن مورد استفاده است می‌توان از دستور ذیل استفاده کرد:
 Scaffold CustomScaffolder EFRepository
به این ترتیب فایل جدید CodeTemplates\Scaffolders\EFRepository\EFRepositoryTemplate.cs.t4 جهت ویرایش به پروژه جاری اضافه خواهد شد.
لیست Scaffolder‌های مهیا با دستور Get-Scaffolder قابل مشاهده است.
مطالب
تهیه خروجی XML از یک بانک اطلاعاتی، توسط EF Code first
نگارش کامل SQL Server امکان تهیه خروجی XML از یک بانک اطلاعاتی را دارد. اما اگر بخواهیم از سایر بانک‌های اطلاعاتی که چنین توابع توکاری ندارند، استفاده کنیم چطور؟ برای تهیه خروجی XML توسط Entity framework و مستقل از نوع بانک اطلاعاتی در حال استفاده، حداقل دو روش وجود دارد:

الف) استفاده از امکانات Serialization توکار دات نت

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace DNTViewer.Common.Toolkit
{
    public static class Serializer
    {
        public static string Serialize<T>(T type)
        {
            var serializer = new XmlSerializer(type.GetType());
            using (var stream = new MemoryStream())
            {
                serializer.Serialize(stream, type);
                stream.Seek(0, SeekOrigin.Begin);
                using (var reader = new StreamReader(stream))
                {
                    return reader.ReadToEnd();
                }
            }
        }
    }
}
در اینجا برای نمونه، لیستی از اشیاء مدنظر خود را تهیه کرده و به متد Serialize فوق ارسال کنید. نتیجه کار، تهیه معادل XML آن است.
امکانات سفارشی سازی محدودی نیز برای XmlSerializer درنظر گرفته شده است؛ برای نمونه قرار دادن ویژگی‌هایی مانند XmlIgnore بالای خواصی که نیازی به حضور آن‌ها در خروجی نهایی XML نمی‌باشد.


ب) استفاده از امکانات LINQ to XML دات نت

روش فوق بدون مشکل کار می‌کند، اما اگر بخواهیم قسمت Reflection خودکار ثانویه آن‌را (برای نمونه جهت استخراج مقادیر از لیست دریافتی) حذف کنیم، می‌توان از LINQ to XML استفاده کرد که قابلیت سفارشی سازی بیشتری را نیز در اختیار ما قرار می‌دهد (کاری که در سایت جاری برای تهیه خروجی XML از بانک اطلاعاتی آن انجام می‌شود).

        private string createXmlFile(string dir)
        {
            var xLinq = new XElement("ArrayOfPost",
                        _blogPosts
                            .AsNoTracking()
                            .Include(x => x.Comments)
                            .Include(x => x.User)
                            .Include(x => x.Tags)
                            .OrderBy(x => x.Id)
                            .ToList()
                            .Select(x => new XElement("Post", postXElement(x)))
                            );

            var xmlFile = Path.Combine(dir, "dot-net-tips-database.xml");
            xLinq.Save(xmlFile);
            return xmlFile;
        }

        private static XElement[] postXElement(BlogPost x)
        {
            return new XElement[]
            {
                new XElement("Id", x.Id),
                new XElement("Title", x.Title),
                new XElement("Body", x.Body),
                new XElement("CreatedOn", x.CreatedOn),
                tagElement(x),
                new XElement("User",
                                new XElement("Id", x.UserId.Value),
                                new XElement("FriendlyName", x.User.FriendlyName))
            }.Where(item => item != null).ToArray();
        }

        private static XElement tagElement(BlogPost x)
        {
            var tags = x.Tags.Any() ?
                            x.Tags.Select(y =>
                                        new XElement("Tag",
                                                new XElement("Id", y.Id),
                                                new XElement("Name", y.Name)))
                                  .ToArray() : null;
            if (tags == null)
                return null;

            return new XElement("Tags", tags);
        }
خلاصه‌ای از نحوه تبدیل اطلاعات لیستی از مطالب را به معادل XML آن در کدهای فوق مشاهده می‌کنید. یک سری نکات ریز نیز باید در اینجا رعایت شوند:
1) کار با یک new XElement که دارای متد Save با فرمت XML نیز هست، شروع می‌شود. مقدار آن‌را مساوی یک کوئری از بانک اطلاعاتی قرار می‌دهیم. این کوئری چون قرار است تنها اطلاعاتی را از بانک اطلاعاتی دریافت کند و نیازی به تغییر در آن‌ها نیست، با استفاده از متد AsNoTracking، حالت فقط خواندنی پیدا کرده است.
2) اطلاعاتی را که نیاز است در فایل نهایی XML وجود داشته باشند، تنها کافی است در قسمت Select این کوئری با فرمت new XElement‌های تو در تو قرار دهیم. به این ترتیب قسمت Relection خودکار XmlSerializer روش مطرح شده در ابتدای بحث دیگر وجود نداشته و عملیات نهایی بسیار سریعتر خواهد بود.
3) چون در این حالت، کار انجام شده دستی است، باید نام‌های گره‌های صحیحی را انتخاب کنیم تا اگر قرار است توسط همان XmlSerializer مجددا کار serializer.Deserialize صورت گیرد، عملیات با شکست مواجه نشود. بهترین کار برای کم شدن سعی و خطاها، تهیه یک لیست اطلاعات آزمایشی و سپس ارسال آن به روش ابتدای بحث است. سپس می‌توان با بررسی خروجی آن مثلا دریافت که روش serializer.Deserialize به صورت پیش فرض به دنبال ریشه‌ای به نام ArrayOfPost برای دریافت لیستی از مطالب می‌گردد و نه Posts یا هر نام دیگری.
4) در کوئری LINQ to Entites نوشته شده، پیش از Select، یک ToList قرار دارد. متاسفانه EF اجازه استفاده مستقیم از Select هایی از نوع XElement را نمی‌دهد و باید ابتدا اطلاعات را تبدیل به LINQ to Objects کرد.
5) در حین تهیه XElement‌ها اگر قرار است عنصری نال باشد، باید آن‌را در خروجی نهایی ذکر نکرد. به این ترتیب serializer.Deserialize بدون نیاز به تنظیمات اضافه‌تری بدون مشکل کار خواهد کرد. در غیراینصورت باید وارد مباحثی مانند تعریف یک فضای نام جدید برای خروجی XML به نام XSI رفت و سپس به کمک ویژگی‌ها، xsi:nil را به true مقدار دهی کرد. اما همانطور که در متد postXElement ملاحظه می‌کنید، برای وارد نشدن به مبحث فضای نام xsi، مواردی که null بوده‌اند، اصلا در آرایه نهایی ظاهر نمی‌شوند و نهایتا در خروجی، حضور نخواهند داشت. به این ترتیب متد ذیل، بدون مشکل و بدون نیاز به تنظیمات اضافه‌تری قادر است فایل XML نهایی را تبدیل به معادل اشیاء دات نتی آن کند.

using System.IO;
using System.Xml;
using System.Xml.Serialization;

namespace DNTViewer.Common.Toolkit
{
    public static class Serializer
    {
        public static T DeserializePath<T>(string xmlAddress)
        {
            using (var xmlReader = new XmlTextReader(xmlAddress))
                {
                    var serializer = new XmlSerializer(typeof(T));
                    return (T)serializer.Deserialize(xmlReader);
                }
        }
    }
}
مطالب
کار با Razor در ASP.NET Core 2.0
پیش نویس: این مقاله ترجمه شده فصل 5 کتاب Pro Asp.Net Core MVC2 می‌باشد.


ایجاد یک پروژه با استفاده Razor

در ادامه با هم یک مثال را با استفاده از Razor ایجاد می‌کنیم. یک پروژه جدید را با قالب Empty و با نام Razor ایجاد می‌کنیم.

مراحل:

1- ابتدا در کلاس startup قابلیت MVC را فعال می‌کنیم؛ با قرار دادن کد زیر در متد ConfigureServices:
 services.AddMvc();
و بعد کد زیر را که مربوط به اجرای پروژه‌ی hello Word است ، از متد Configure حذف می‌کنیم:
app.Run(async (context) =>
{
   await context.Response.WriteAsync("Hello World!");
});
در نهایت محتویات  فایل StartUp به صورت زیر می‌باشد:

namespace Razor
{
    public class Startup
    {
        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }

            //app.Run(async (context) =>
            //{
            //    await context.Response.WriteAsync("Hello World!");
            //});
        }
    }
}


ایجاد یک Model
 یک پوشه جدید را به نام Models ایجاد و بعد در این پوشه یک کلاس را به نام Product ایجاد می‌کنیم و کدهای زیر را در آن قرار میدهیم:
namespace Razor.Models
{
    public class Product
    {
        public int ProductID { get; set; }
        public string Name { get; set; }
        public string Description { get; set; }
        public decimal Price { get; set; }
        public string Category { set; get; }
    }
}

ایجاد Controller
تنظیمات پیشفرض را در فایل Startup انجام داده‌ایم. درخواست‌هایی را که توسط کاربر ارسال میشوند، به controller پیشفرضی که نامش در اینجا Home است، ارسال می‌کند. حالا ما یک پوشه جدید را به نام Controllers ایجاد می‌کنیم و در آن یک کنترلر جدید را به نام HomeController ایجاد می‌کنیم و کدهای زیر را در آن قرار میدهیم:
namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        // GET: /<controller>/
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            return View(myProduct);
        }
    }
}
در این کلاس یک Action Method را به نام index ایجاد می‌کنیم. سپس در آن یک شیء را از مدل ایجاد و مقدار دهی و آن‌را به View ارسال می‌کنیم تا در زمان بارگذاری View از این شیء استفاده نماییم. نیاز نیست نام View را مشخص کنید. به صورت پیشفرض نام View با نام اکشن متد یکسان می‌باشد.

 
ایجاد View
 برای ایجاد یک View پیشفرض برای Action Method فوق در پوشه Views/Home یک MVC View Page (Razor View Page) را به نام Index.schtml ایجاد می‌کنیم.
- نکته1: پوشه View و داخل آن Home را ایجاد کنید.
- نکته2: معادل MVC View Page در نسخه جدید، Razor View می‌باشد. اگر در لیست این آیتم را انتخاب کنید، در توضیحات پنل سمت راست میتوانید این مطلب را مشاهده کنید.
- نکته3: دقت نمایید برای اینکه پروژه net Core2. باشد و تمام مشخصات موردنظر را داشته باشد، باید نگارش ویژوال استودیو VS 2017.15.6.6 و یا بیشتر باشد.
 
@model Razor.Models.Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
    Content will go here
</body>
</html>

تا اینجا ما یک پروژه ساده را ایجاد نموده‌ایم که قابلیت استفاده‌ی از Razor را هم دارد. در ادامه نحوه‌ی استفاده از امکانات Razor شرح داده میشوند.


استفاده از Model در یک View
برای استفاده از شیء مدل در View، باید در View به آن شیء و مشخصات آن دسترسی داشته باشیم که این دسترسی را Razor با استفاده از کاراکتر @ برای ما ایجاد می‌کند. برای اتصال به Model از عبارت model@ (حتما باید حروف کوچک باشد) استفاده می‌کنیم و برای دسترسی به مشخصات مدل از عبارت Model@ (حتما باید حرف اول آن بزرگ باشد) استفاده می‌کنیم. به کد زیر دقت کنید:

@model Razor.Models.Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
  @Model.Name
</body>
</html>
خط اولی که در View تعریف شده است، با استفاده از عبارت model@ مانند تعریف نوع مدل می‌باشد و کار اتصال مدل به View را انجام میدهد و همین خط باعث میشود زمانی که شما در تگ body عبارت Model@ وبعد دات (.) را میزنید، لیست خصوصیات آن مدل ظاهر میشوند. لیست شدن خصوصیات بعد از دات(.) یکی از کارهای پیشفرض ویژوال استودیو می‌باشد؛ برای اینکه از خطاهای احتمالی کاربر جلوگیری کند.

نتیجه خروجی بالا مانند زیر می‌باشد:

 



معرفی View Imports

زمانیکه بخواهیم به یک کلاس در View دسترسی داشته باشیم، باید فضای نام آن کلاس را مانند کد زیر در بالای View اضافه کنیم. حالا اگر بخواهیم به چند کلاس دسترسی داشته باشیم، باید این کار را به ازای هر کلاس در هر View انجام دهیم که سبب ایجاد کدهای اضافی در View‌ها میشود. برای بهبود این وضعیت می‌توانید یک کلاس View Import را در پوشه‌ی Views ایجاد کنید و تمام فضاهای نام را در آن قرار دهید. با اینکار تمام فضاهای نامی که در این کلاس View Import قرار گرفته‌اند، در تمام Viewهای موجود در پوشه Views قابل دسترسی خواهند بود.

در پوشه View راست کلیک کرده و گزینه Add و بعد New Item را انتخاب می‌کنیم و در کادر باز شده، آیتم MVC View Import Page (در نسخه جدید نام آن  Razor View Imports است) انتخاب می‌کنیم. ویژوال استودیو به صورت پیش فرض نام ViewImports.cshtml_ را برای آن قرار میدهد.


نکته: استاندارد نام گذاری این View این می‌باشد که ابتدای آن کاراکتر (_) حتما وجود داشته باشد.
 
در کلاس تعریف شده با استفاده از عبارت using@ فضای نام‌های خود را قرار میدهیم؛ مانند زیر:
 @using Razor.Models
در این کلاس شما فقط میتوانید فضاهای نام را مانند بالا قرار دهید. پس از آم قسمت فضاهای نام اضافی در Viewها قابل حذف میشوند و در این حالت فقط نام کلاس مدل را در بالای فرم قرار میدهیم مانند زیر:
@model Product
@{
    Layout = null;
}

<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width"/>
    <title>Index</title>
</head>
<body>
  @Model.Name
</body>
</html>


Layout ها

یکی دیگر از عبارت‌های مهم Razor که در فایل Index وجود دارد، عبارت زیر است:
@{
    Layout = null;
}
شما می‌توانید در بین {} کدهای سی شارپ را قرار دهید. حالا مقدار Layout را مساوی نال قرار داده‌ایم که بگوییم View مستقلی است و از قالب مشخصی استفاده نمی‌کند.

از Layout برای طراحی الگوی Viewها استفاده می‌کنیم. اگر بخواهیم برای View ها یک قالب طراحی کنیم و این الگو بین تمام یا چندتای از آن‌ها مشترک باشد، کدهای مربوط به الگو را با استفاده از Layout ایجاد می‌کنیم و از آن در View ها استفاده می‌کنیم. اینکار برای جلوگیری از درج کدهای تکراری قالب در برنامه انجام میشود. با اینکار اگر بخواهیم در الگو تغییری را انجام دهیم، این تغییر را در یک قسمت انجام میدهم و سپس به تمام Viewها اعمال میشود.
 
Layout
طرحبندی  Viewهای برنامه بطور معمول بین چند View مشترک است و طبق استاندارد ویژوال استودیو در پوشه‌ی Views/Shared قرار میگیرد. برای ایجاد Layout، روی پوشه Views/shared راست کلیک کرده و بعد گزینه Add وبعد NewItem و سپس گزینه MVC View Layout Page (نام آن در نسخه جدید Razor Layout است) را انتخاب می‌کنیم و ابتدای نام آن را به صورت پیشفرض کاراکتر (_) قرار میدهیم.
 


هنگام ایجاد این فایل توسط ویژوال استودیو، کدهای زیر به صورت پیش فرض در فایل ایجاد شده وجود دارند: 
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
</head>
<body>
    <div>
        @RenderBody()
    </div>
</body>
</html>
طرحبندی‌ها فرم خاصی از View هستند و دو عبارت @ در کدهای آن وجود دارد. در اینجا فراخوانی RenderBody@ سبب درج محتویات View مشخص شده توسط Action Method در این مکان می‌شود. عبارت دیگری که در اینجا وجود دارد، ViewBag است که برای مشخص کردن عنوان در اینجا استفاده شده‌است.
ViewBag ویژگی مفیدی است که اجازه می‌دهد تا مقادیر و داده‌ها در برنامه گردش داشته باشند و در این مورد بین یک View و Layout منتقل شوند. در ادامه خواهید دید وقتی Layout را به یک نمایه اعمال می‌کنیم، این مورد چگونه کار می‌کند.

عناصر HTML در یک Layout به هر View که از آن استفاده می‌کند، اعمال و توسط آن یک الگو برای تعریف محتوای معمولی ارائه می‌شود؛ مانند کدهای زیر. من برخی از نشانه گذاری‌های ساده را به Layout اضافه کردم تا اثر قالب آن آشکارتر شود:
<!DOCTYPE html>

<html>
<head>
    <meta name="viewport" content="width=device-width" />
    <title>@ViewBag.Title</title>
    <style>
        #mainDiv {
            padding: 20px;
            border: solid medium black;
            font-size: 20pt
        }
    </style>
</head>
<body>
    <h1>Product Information</h1>
    <div id="mainDiv">
        @RenderBody()
    </div>
</body>
</html>
در اینجا یک عنصر عنوان و همچنین بعضی از CSS‌ها را به عنصر div که حاوی عبارت RenderBody@ است، اضافه کرده‌ام؛ فقط برای اینکه مشخص شود، چه محتوایی از طرحبندی سایت می‌آید و چه چیزی از View.
 

اعمال Layout

برای اعمال کردن Layout به یک View، نیاز است مشخصه Layout آن‌را مقدار دهی و سپس Htmlهای اضافی موجود در آن‌را مانند المنت‌های head و Body حذف کنید؛ همانند کدهای زیر:
@model Product
@{
    Layout = "_BasicLayout";
    ViewBag.Title = "Product";
}
در خاصیت Layout، مقدار را برابر نام فایل Layout، بدون پسوند cshtml آن قرار میدهیم. Razor در مسیر پوشه Views/shared و پوشه Views/Home فایل Layout را جستجو می‌کند.
در اینجا عبارت ViewBag.Title را نیز مقدار دهی می‌کنیم. زمانیکه فایل فراخوانی میشود، عنوان آن صفحه با این مقدار، جایگزین خواهد شد.
تغییرات این View بسیار چشمگیر است؛ حتی برای چنین برنامه ساده‌ای. طرحبندی شامل تمام ساختار مورد نیاز برای هر پاسخ HTML است که View را به صورت یک محتوای پویا ارائه می‌دهد و داده‌ها را به کاربر منتقل می‌کند. هنگامیکه MVC فایل Index.cshtmal را پردازش می‌کند، این طرحبندی برای ایجاد پاسخ HTML نهایی یکپارچه می‌شود؛ مانند عکس زیر:
 


 
View Start

بعضی موارد هنوز در برنامه وجود دارند که می‌توان کنترل بیشتری بر روی آن‌ها داشته باشید. مثلا اگر بخواهیم نام یک فایل layout را تغییر دهیم، مجبور هستیم تمام Viewهایی را که از آن Layout استفاده می‌کنند، پیدا کنید و نام Layout استفاده شده در آن‌ها را تغییر دهیم. اینکار احتمال خطای بالایی دارد و امکان دارد بعضی View ها از قلم بیفتند و برنامه دچار خطا شود. بنابراین با استفاده از View Start می‌توانیم این مشکل را برطرف کنیم. وقتی نام Layout تغییر کرد، تنها کافی است نام آن‌را در View Start تغییر دهیم. اکنون زمانیکه برنامه را اجرا می‌کنیم، MVC به دنبال فایل View Start می‌گردد و اگر اطلاعاتی داشته باشد، آن را اجرا می‌کند و الویت این فایل از تمام فایل‌های دیگر بیشتر است و ابتدا تمام آنها اجرا میشوند.

برای ایجاد یک فایل شروع مشاهده، روی پوشه‌ی Views کلیک راست کرده و گزینه add->New Items را انتخاب می‌کنیم و از پنجره باز شده گزینه ( Razor View Start ) Mvc View Start Page را انتخاب می‌کنیم؛ مانند تصویر زیر:


ویژوال استودیو به صورت پیش فرض نام ViewStart.cshtml_ را به عنوان نام آن قرار میدهد؛ شما گزینه‌ی Create را در این حالت انتخاب کنید. محتویات فایل ایجاد شده به صورت زیر می‌باشد:
@{
    Layout = "_Layout";
}
برای اعمال Layout جدید به تمام Viewها، مقدار Layout را معادل طرحبندی خود تغییر میدهیم؛ مانند کد زیر: 
@{
    Layout = "_BasicLayout";
}
از آنجا که فایل View Start دارای مقداری برای Layout می‌باشد، می‌توانیم عبارت‌های مربوطه را در Index.cshtml‌ها حذف کنیم:
@model Product
@{
    ViewBag.Title = "Product";
}
در اینجا لازم نیست مشخص کنیم که من می‌خواهم از فایل View Start استفاده کنم. MVC این فایل را پیدا خواهد کرد و از محتویات آن به طور خودکار استفاده می‌کند. البته باید دقت داشت که مقادیر تعریف شده‌ی در فایل View اولویت دارند و باعث میشوند با معادل‌های فایل View Start جایگزین شوند.

شما همچنین می‌توانید چندین فایل View Start را برای تنظیم مقادیر پیش فرض قسمت‌های مختلف برنامه، استفاده کنید. یک فایل Razor همواره توسط نزدیک‌ترین فایل View start، پردازش می‌شود. به این معنا که شما می‌توانید تنظیمات پیش فرض را با افزودن یک فایل View Start به پوشه Views / Home و یا Views / Shared لغو کنید.

نکته: درک تفاوت میان حذف محتویات فایل View Start یا مساوی Null قرار دادن آن مهم است. اگر View شما مستقل است و شما نمی‌خواهید از آن استفاده کنید، بنابراین مقدار Layout آن‌را صریحا برابر Null قرار دهید. اگر مقدار دهی صریح شما مشخصه Layout را نادیده بگیرید، Mvc فرض می‌کند که میخواهید layout را داشته باشید و مقدار آن را از فایل View Start تامین می‌کند.
 

استفاده از عبارت‌های شرطی در Razor
 
حالا که من اصول و مبانی View و Layout را به شما نشان دادم، قصد دارم به انواع مختلفی از اصطلاحات که Razor آن‌ها را پشتیبانی می‌کند و نحوه استفاده‌ی از آنها را برای ایجاد محتوای نمایشی، ارائه دهم. در یک برنامه MVC، بین نقش‌هایی که توسط View و Action متدها انجام می‌شود، جدایی روشنی وجود دارد. در اینجا قوانین ساده‌ای وجود دارند که در جدول زیر مشخص شده‌اند:

کامپوننت 
انجام میشود 
انجام نمیشود 
  Action Method    یک شیء ViewModel را به View ارسال می‌کند.
  یک فرمت داده را به View ارسال می‌کند.
  View    از شیء ViewModel برای ارائه محتوا به کاربر استفاده می‌کند.
  هر جنبه‌ای از شیء View Model مشخصات را تغییر می‌دهد.
 
برای به دست آوردن بهترین نتیجه از MVC، نیاز به تفکیک و جداسازی بین قسمت‌های مختلف برنامه را دارید. همانطور که می‌بینید، می‌توانید کاملا با Razor کار کنید و این نوع فایل‌ها شامل دستورالعمل‌های سی شارپ نیز هستند. اما شما نباید از Razor برای انجام منطق کسب و کار استفاده کنید و یا هر گونه اشیاء Domain Model خود را دستکاری کنید. کد زیر نشان میدهد که یک عبارت جدید به View اضافه میشود:
*@
@model Product
@{

    ViewBag.Title = "Product";
}
<p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p>
می‌توان برای خصوصیت price، در اکشن متد فرمتی را تعریف و بعد آن را به View ارسال کنیم. این روش کار می‌کند، اما استفاده از این رویکرد منافع الگوی MVC را تضعیف می‌کند و توانایی من برای پاسخ دادن به تغییرات در آینده را کاهش می‌دهد. باید به یاد داشته باشید که در ASP NET Core MVC، استفاده مناسب از الگوی MVC اجتناب ناپذیر است و شما باید از تاثیر تصمیمات طراحی و کدگذاری که انجام می‌دهید مطلع باشید.
 

پردازش داده‌ها در مقابل فرمت

تفاوت بین پردازش داده و قالب بندی داده مهم است.
- نمایش فرمت داده‌ها: به همین دلیل در آموزش قبل من یک نمونه از شیء کلاس Product را برای View ارسال کرده‌ام و نه فرمت خاص یک شیء را به صورت یک رشته نمایشی.
- پردازش داده: انتخاب اشیاء داده‌‌ای برای نمایش، مسئولیت کنترلر است و در این حالت مدلی را برای دریافت و تغییر داده مورد نیاز، فراخوانی می‌کند.
گاهی سخت است که متوجه شویم کدی جهت پردازش داده است و یا فرمت آن.


اضافه نمودن مقدار داده ای

ساده‌ترین کاری را که می‌توانید با یک عبارت Razor انجام دهید این است که یک مقدار داده را در نمایش دهید. رایج‌ترین کار برای انجام آن، استفاده از عبارت Model@ است. ویوو Index یک مثال از این مورد است؛ شبیه به این مورد:
 <p>Product Name: @Model.Name</p>
شما همچنین می‌توانید یک مقدار را با استفاده قابلیت ViewBag نیز به View ارسال نمایید که از این قابلیت در Layout برای تنظیم کردن محتوای عنوان استفاده کردیم. اما در حالت زیر یک مدل نوع دار را به سمت View ارسال کرده‌ایم:
using Microsoft.AspNetCore.Mvc;
using Razor.Models;


namespace Razor.Controllers
{
    public class HomeController : Controller
    {
        // GET: /<controller>/
        public ViewResult Index()
        {
            Product myProduct = new Product
            {
                ProductID = 1,
                Name = "Kayak",
                Description = "A boat for one person",
                Category = "Watersports",
                Price = 275M
            };
            return View(myProduct);
        }
    }
}

خصوصیت ViewBag یک شیء پویا را باز می‌گرداند که می‌تواند برای تعیین خواص دلخواهی مورد استفاده قرار گیرد. از آنجا که ویژگی ViewBag پویا است، لازم نیست که نام خصوصیات را پیش از آن اعلام کنم. اما این بدان معنا است که ویژوال استودیو قادر به ارائه پیشنهادهای تکمیل کننده برای ViewBag نیست.
در مثال زیر از یک مدل نوع دار و مزایای به همراه آن استفاده شده‌است: 
 <p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p> <p>Stock Level: @ViewBag.StockLevel</p>
نتیجه آن‌را در زیر می‌توانید مشاهده کنید:



تنظیم مقادیر مشخص

شما همچنین می‌توانید از عبارات Razor برای تعیین مقدار عناصر، استفاده کنید:
@model Product
@{

    ViewBag.Title = "Product";
}
p>Product Name: @Model.Name</p> <p>Product Price: @($"{Model.Price:C2}")</p> 
<p>Stock Level: @ViewBag.StockLevel</p>
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">    
<p>Product Name: @Model.Name</p>    
<p>Product Price: @($"{Model.Price:C2}")</p>   
 <p>Stock Level: @ViewBag.StockLevel</p> 
</div>
در اینجا از عبارات Razor، برای تعیین مقدار برای برخی از ویژگی‌های داده در عنصر div استفاده کرده‌ام.

نکته: ویژگی‌های داده‌ها که نام آنها *-data است، روشی برای ایجاد ویژگی‌های سفارشی برای سال‌ها بوده است و بعنوان بخشی از استاندارد HTML5 است. عموما کدهای جاوا اسکریپت از آن‌ها برای یافتن اطلاعات استفاده می‌کنند.

اگر برنامه را اجرا کنید و به منبع HTML که به مرورگر فرستاده شده نگاهی بیندازید، خواهید دید که Razor مقادیر صفات را تعیین کرده است؛ مانند این:
<div data-productid="1" data-stocklevel="2">    <p>Product Name: Kayak</p>    <p>Product Price: £275.00</p>    <p>Stock Level: 2</p> </div>


استفاده از عبارت‌های شرطی

Razor قادر به پردازش عبارات شرطی است. در ادامه کدهای Index View را که در آن دستورات شرطی اضافه شده‌اند می‌بینید:

@model Product
@{ ViewBag.Title = "Product Name"; }
<div data-productid="@Model.ProductID" data-stocklevel="@ViewBag.StockLevel">  
  <p>Product Name: @Model.Name</p>   
 <p>Product Price: @($"{Model.Price:C2}")</p> 
   <p>Stock Level:       
 @switch (ViewBag.StockLevel)
{
    case 0:@:Out of Stock                break;           
    case 1:          
    case 2:        
    case 3:            
    <b>Low Stock (@ViewBag.StockLevel)</b>         
       break;      
    default:            
    @: @ViewBag.StockLevel in Stock          
      break;      
  }    
</p>
</div>


برای شروع یک عبارت شرطی، یک علامت @ را در مقابل کلمه کلیدی if یا swicth سی شارپ قرار دهید. سپس بخش کد را داخل } قرار می‌دهیم. درون قطعه کد Razor، می‌توانید عناصر HTML و مقادیر داده را در خروجی نمایش دهید؛ مانند:
 <b>Low Stock (@ViewBag.StockLevel)</b>
در اینجا لازم نیست عناصر یا عبارات را در نقل قول قرار دهیم و یا آنها را به روش خاصی تعریف کنیم. موتور Razor این را به عنوان خروجی برای پردازش تفسیر خواهد کرد.
با این حال، اگر می‌خواهید متن واقعی را در نظر بگیرید و دستورات Razor را لغو کنید،‌می‌توانید از :@ استفاده کنید تا عین آن عبارت درج شود.