نظرات مطالب
به روز رسانی ساده‌تر اجزاء ارتباطات در EF Code first به کمک GraphDiff
ضمن تشکر از مطلب خوبتون
آیا امکانش هست که بدون داشتن AssociatedEntity‌ها هم از GraphDiff استفاده کرد؟ منظور وقتیست که با نوع Generic کار می‌کنیم نه با یک Entity مشخص
نظرات مطالب
خودکار کردن تعاریف DbSetها در EF Code first
در .net core2 خطای ambigious match found میده در این خط
var entityMethod = typeof (DbModelBuilder).GetMethod("Entity");
چه شکلی میشه دقیقا مشخص کرد منظور کدوم متد Entity هست؟
مطالب
شروع به کار با EF Core 1.0 - قسمت 1 - برپایی تنظیمات اولیه
در ادامه‌ی سری «ارتقاء به ASP.NET Core 1.0» اگر بخواهیم مباحث اعتبارسنجی کاربران و ASP.NET Identity مخصوص آن‌را بررسی کنیم، نیاز است ابتدا مباحث Entity framework Core 1.0 را بررسی کنیم. به همین جهت در طی چند قسمت مباحث پایه‌ای کار با EF Core 1.0 را در ASP.NET Core 1.0، بررسی خواهیم کرد. بنابراین پیشنیاز ضروری این مباحث، مطالعه‌ی سری «ارتقاء به ASP.NET Core 1.0» است و در آن از مباحثی مانند چگونگی کار با فایل‌های کانفیگ جدید، تزریق وابستگی‌ها و سرویس‌ها، فعال سازی سرویس Logging، فعال سازی صفحات مخصوص توسعه دهنده‌ها و ... در ASP.NET Core 1.0 استفاده خواهد شد.


EF Core چیست؟

EF Core یک ORM یا object-relational mapper چندسکویی است که امکان کار با بانک‌های اطلاعاتی مختلف را از طریق اشیاء دات نتی میسر می‌کند. توسط آن قسمت عمده‌ی کدهای مستقیم کار با بانک‌های اطلاعاتی حذف شده و تبدیل به کدهای دات نتی می‌شوند. مزیت این لایه‌ی Abstraction اضافی (لایه‌ای بر روی کدهای مستقیم لایه ADO.NET زیرین)، امکان تعویض بانک اطلاعاتی مورد استفاده، تنها با تغییر کدهای آغازین برنامه‌است؛ بدون نیاز به تغییری در سایر قسمت‌های برنامه. همچنین کار با اشیاء دات نتی و LINQ، مزایایی مانند تحت نظر قرار گرفتن کدها توسط کامپایلر و برخورداری از ابزارهای Refactoring پیشرفته را میسر می‌کنند. به علاوه SQL خودکار تولیدی توسط آن نیز همواره پارامتری بوده و مشکلات حملات تزریق SQL در این حالت تقریبا به صفر می‌رسند (اگر مستقیما SQL نویسی نکنید و صرفا از LINQ استفاده کنید). مزیت دیگر همواره پارامتری بودن کوئری‌ها، رفتار بسیاری از بانک‌های اطلاعاتی با آن‌ها همانند رویه‌های ذخیره شده است که به عدم تولید Query plan‌های مجزایی به ازای هر کوئری رسیده منجر می‌شود که در نهایت سبب بالا رفتن سرعت اجرای کوئری‌ها و مصرف حافظه‌ی کمتری در سمت سرور بانک اطلاعاتی می‌گردد.


تفاوت EF Core با نگارش‌های دیگر Entity framework در چیست؟

سورس باز بودن
EF از نگارش‌های آخر آن بود که سورس باز شد؛ اما EF Core از زمان نگارش‌های پیش نمایش آن به صورت سورس باز در GitHub قابل دسترسی است.

چند سکویی بودن
EF Core برخلاف EF 6.x (آخرین نگارش مبتنی بر Full Framework آن)، نه تنها چندسکویی است و قابلیت اجرای بر روی Mac و لینوکس را نیز دارا است، به علاوه امکان استفاده‌ی از آن در انواع و اقسام برنامه‌های دات نتی مانند UWP یا Universal Windows Platform و Windows phone که پیشتر با EF 6.x میسر نبود، وجود دارد. لیست این نوع سکوها و برنامه‌های مختلف به شرح زیر است:
 • All .NET application (Console, ASP.NET 4, WinForms, WPF)
 • Mac and Linux applications (Mono)
 • UWP (Universal Windows Platform)
 • ASP.NET Core applications
 • Can use EF Core in Windows phone and Windows store app

افزایش تعداد بانک‌های اطلاعاتی پشتیبانی شده
در EF Full یا EF 6.x، هدف اصلی، تنها کار با بانک‌های اطلاعاتی رابطه‌‌ای بود و همچنین مایکروسافت صرفا نگارش‌های مختلف SQL Server را به صورت رسمی پشتیبانی می‌کرد و برای سایر بانک‌های اطلاعاتی دیگر باید از پروایدرهای ثالث استفاده کرد.
در EF Core علاوه بر افزایش تعداد پروایدرهای رسمی بانک‌های اطلاعاتی رابطه‌ای، پشتیبانی از بانک‌های اطلاعاتی NoSQL هم اضافه شده‌است؛ به همراه پروایدر ویژه‌‌ای به نام In Memory جهت انجام ساده‌تر Unit testing. کاری که با نگارش‌های پیشین EF به سادگی و از روز اول پشتیبانی نمی‌شد.

حذف و یا عدم پیاده سازی تعدادی از قابلیت‌های EF 6.x
اگر موارد فوق جزو مهم‌ترین مزایای کار با EF Core باشند، باید درنظر داشت که به علت حذف و یا تقلیل یافتن یک سری از ویژگی‌ها در NET Core.، مانند Reflection (جهت پشتیبانی از دات نت در سکوهای مختلف کاری و خصوصا پشتیبانی از حالتی که کامپایلر مخصوص برنامه‌های UWP نیاز دارد تمام نوع‌ها را همانند زبان‌های C و ++C، در زمان کامپایل بداند)، یک سری از قابلیت‌های EF 6.x مانند Lazy loading هنوز در EF Core پشتیبانی نمی‌شوند. لیست کامل و به روز شده‌ی این موارد را در اینجا می‌توانید مطالعه کنید.
بنابراین امکان انتقال برنامه‌های EF 6.x به EF Core 1.0 عموما وجود نداشته و نیاز به بازنویسی کامل دارند. هرچند بسیاری از مفاهیم آن با EF Code First یکی است.


برپایی تنظیمات اولیه‌ی EF Core 1.0 در یک برنامه‌ی ASP.NET Core 1.0

برای نصب تنظیمات اولیه‌ی EF Core 1.0 در یک برنامه‌ی ASP.NET Core 1.0، جهت کار با مشتقات SQL Server (و SQL LocalDB) نیاز است سه بسته‌ی ذیل را نصب کرد (از طریق منوی Tools -> NuGet Package Manager -> Package Manager Console):
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer
PM> Install-Package Microsoft.EntityFrameworkCore.Tools -Pre
PM> Install-Package Microsoft.EntityFrameworkCore.SqlServer.Design
البته در این قسمت صرفا از بسته‌ی اول که جهت اتصال به SQL Server است استفاده می‌کنیم. بسته‌های دیگر را در قسمت‌های بعد، برای به روز رسانی اسکیمای بانک اطلاعاتی (مباحث Migrations) و مباحث scaffolding استفاده خواهیم کرد.
پس از اجرای سه دستور فوق، تغییرات مداخل فایل project.json برنامه به صورت ذیل خواهند بود:
{
    "dependencies": {
       // same as before
        "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
        "Microsoft.EntityFrameworkCore.Tools": "1.0.0-preview2-final",
        "Microsoft.EntityFrameworkCore.SqlServer.Design": "1.0.0"
    }
}
این مداخلی که توسط نیوگت اضافه شده‌اند، نیاز به اصلاح دارند؛ به این صورت:
{
    "dependencies": {
       // same as before
        "Microsoft.EntityFrameworkCore.SqlServer": "1.0.0",
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "type": "build"
        },
        "Microsoft.EntityFrameworkCore.SqlServer.Design": {
            "version": "1.0.0",
            "type": "build"
        }
    },

    "tools": {
       // same as before
        "Microsoft.EntityFrameworkCore.Tools": {
            "version": "1.0.0-preview2-final",
            "imports": [
                "portable-net45+win8"
            ]
        }   
   }
}
نیاز است در قسمت dependencies مشخص کنیم که ابزارهای اضافه شده مخصوص build هستند و نه اجرای برنامه. همچنین قسمت tools را باید با Microsoft.EntityFrameworkCore.Tools مقدار دهی کرد تا بتوان از این ابزار در خط فرمان، جهت اجرای فرامین migrations استفاده کرد.
بنابراین از همین ابتدای کار، بدون مراجعه‌ی به Package Manager Console، چهار تغییر فوق را به فایل project.json اعمال کرده و آن‌را ذخیره کنید؛ تا کار به روز رسانی و نصب بسته‌ها، به صورت خودکار و همچنین صحیحی انجام شود.


فعال سازی صفحات مخصوص توسعه دهنده‌های EF Core 1.0

در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 5 - فعال سازی صفحات مخصوص توسعه دهنده‌ها» با تعدادی از اینگونه صفحات آشنا شدیم. برای EF Core نیز بسته‌ی مخصوصی به نام Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore وجود دارد که امکان فعال سازی صفحه‌ی نمایش خطاهای بانک اطلاعاتی را میسر می‌کند. بنابراین ابتدا به فایل project.json مراجعه کرده و این بسته را اضافه کنید:
{
    "dependencies": {
       // same as before
        "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "1.0.0"
    }
}
سپس می‌توان متد جدید UseDatabaseErrorPage را در متد Configure کلاس آغازین برنامه، فراخوانی کرد:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
   if (env.IsDevelopment())
   {
      app.UseDatabaseErrorPage();
   }
با فعال سازی این صفحه، اگر در حین توسعه‌ی برنامه و اتصال به بانک اطلاعاتی، خطایی رخ دهد، بجای مشاهده‌ی یک صفحه‌ی خطای عمومی (اگر UseDeveloperExceptionPage را فعال کرده باشید)، اینبار ریز جزئیات بیشتری را به همراه توصیه‌هایی از طرف تیم EF مشاهده خواهید کرد.


تعریف اولین Context برنامه و مشخص سازی رشته‌ی اتصالی آن


در این تصویر، زیر ساخت نگاشت‌های EF Core را مشاهده می‌کنید. در سمت چپ، ظرفی را داریم به نام DB Context که در برگیرنده‌ی Db Setها است. در سمت راست که بیانگر ساختار کلی یک بانک اطلاعاتی است، معادل این‌ها را مشاهده می‌کنیم. هر Db Set به یک جدول بانک اطلاعاتی نگاشت خواهد شد و متشکل است از کلاسی به همراه یک سری خواص که این‌ها نیز به فیلدها و ستون‌های آن جدول در سمت بانک اطلاعاتی نگاشت می‌شوند.
بنابراین برای شروع کار، پوشه‌ای را به نام Entities به پروژه اضافه کرده و سپس کلاس ذیل را به آن اضافه می‌کنیم:
namespace Core1RtmEmptyTest.Entities
{
    public class Person
    {
        public int PersonId { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}
کلاس Person بیانگر ساختار جدول اشخاص بانک اطلاعاتی است. برای اینکه این کلاس را تبدیل و نگاشت به یک جدول کنیم، نیاز است آن‌را به صورت یک DbSet در معرض دید EF Core قرار دهیم و اینکار در کلاسی که از DbContex مشتق می‌شود، صورت خواهد گرفت:
using Microsoft.EntityFrameworkCore;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        {
        }

        public DbSet<Person> Persons { get; set; }
    }
}
بنابراین در ادامه کلاس جدید ApplicationDbContext را که از کلاس پایه DbContext مشتق می‌شود تعریف کرده و سپس کلاس Person را به صورت یک DbSet در معرض دید EF Core قرار می‌دهیم.
سازنده‌ی این کلاس نیز به نحو خاصی تعریف شده‌است. اگر به سورس‌های EF Core مراجعه کنیم، کلاس پایه‌ی DbContext دارای دو سازنده‌ی با و بدون پارامتر است:
protected DbContext()
   : this((DbContextOptions) new DbContextOptions<DbContext>())
{
}

public DbContext([NotNull] DbContextOptions options)
{
  // …
}
اگر از سازنده‌ی بدون پارامتر استفاده کنیم و برای مثال در کلاس ApplicationDbContext فوق، به طور کامل سازنده‌ی تعریف شده را حذف کنیم، باید به نحو ذیل تنظیمات بانک اطلاعاتی را مشخص کنیم:
using Microsoft.EntityFrameworkCore;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        public DbSet<Person> Persons { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(@"... connection string ...");
        }
    }
}
در این حالت باید متد OnConfiguring را override و یا بازنویسی کنیم، تا بتوان از اول مشخص کرد که قرار است از پروایدر SQL Server استفاده کنیم و ثانیا رشته‌ی اتصالی به آن چیست.
اما چون در یک برنامه‌ی ASP.NET Core، کار ثبت سرویس مربوط به EF Core، در کلاس آغازین برنامه انجام می‌شود و در آنجا به سادگی می‌توان به خاصیت Configuration برنامه دسترسی یافت و توسط آن رشته‌ی اتصالی را دریافت کرد، مرسوم است از سازنده‌ی با پارامتر DbContext به نحوی که در ابتدا عنوان شد، استفاده شود.
بنابراین در ادامه، پس از مطالعه‌ی مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» به فایل appsettings.json مراجعه کرده و تنظیمات رشته‌ی اتصالی برنامه را به صورت ذیل در آن مشخص می‌کنیم:
{
    "ConnectionStrings": {
        "ApplicationDbContextConnection": "Data Source=(local);Initial Catalog=TestDbCore2016;Integrated Security = true"
    }
}
باید دقت داشت که نام این مداخل کاملا اختیاری هستند و در نهایت باید در کلاس آغازین برنامه به صورت صریحی مشخص شوند.
در اینجا به وهله‌ی پیش فرض SQL Server اشاره شده‌است؛ از حالت اعتبارسنجی ویندوزی SQL Server استفاده می‌شود و بانک اطلاعاتی جدیدی به نام TestDbCore2016 در آن مشخص گردیده‌است.

پس از تعریف رشته‌ی اتصالی، متد OnConfiguring را از کلاس ApplicationDbContext حذف کرده و از همان نگارش دارای سازنده‌ی با پارامتر آن استفاده می‌کنیم. برای اینکار به کلاس آغازین برنامه مراجعه کرده و توسط متد AddDbContext این Context را به سرویس‌های ASP.NET Core معرفی می‌کنیم:
    public class Startup
    {
        public IConfigurationRoot Configuration { set; get; }

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                                .SetBasePath(env.ContentRootPath)
                                .AddJsonFile("appsettings.json", reloadOnChange: true, optional: false)
                                .AddJsonFile($"appsettings.{env}.json", optional: true);
            Configuration = builder.Build();
        }

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConfigurationRoot>(provider => { return Configuration; });
            services.AddDbContext<ApplicationDbContext>(options =>
            {
                options.UseSqlServer(Configuration["ConnectionStrings:ApplicationDbContextConnection"]);
            });
در اینجا جهت یادآوری مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایل‌های config» نحوه‌ی وهله سازی خاصیت Configuration که در متد UseSqlServer مورد استفاده قرار گرفته‌است، نیز ذکر شده‌است.
بنابراین قسمت options.UseSqlServer را یا در اینجا مقدار دهی می‌کنید و یا از طریق بازنویسی متد OnConfiguring کلاس Context برنامه.


یک نکته: امکان تزریق IConfigurationRoot به کلاس Context برنامه وجود دارد

با توجه به اینکه Context برنامه را به صورت یک سرویس به ASP.NET Core معرفی کردیم، امکان تزریق وابستگی‌ها نیز در آن وجود دارد. یعنی بجای روش فوق، می‌توان IConfigurationRoot را به سازنده‌ی کلاس Context برنامه نیز تزریق کرد:
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;

namespace Core1RtmEmptyTest.Entities
{
    public class ApplicationDbContext : DbContext
    {
        private readonly IConfigurationRoot _configuration;

        public ApplicationDbContext(IConfigurationRoot configuration)
        {
            _configuration = configuration;
        }

        //public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options)
        //{
        //}

        public DbSet<Person> Persons { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            optionsBuilder.UseSqlServer(_configuration["ConnectionStrings:ApplicationDbContextConnection"]);
        }
    }
}
با توجه به اینکه IConfigurationRoot در کلاس ConfigureServices به صورت Singleton، به مجموعه‌ی سرویس‌های برنامه معرفی شده‌است، از آن در تمام کلاس‌های برنامه که تحت نظر سیستم تزریق وابستگی‌های توکار ASP.NET Core هستند، می‌توان استفاده کرد.
در این حالت متد ConfigureServices کلاس آغازین برنامه، چنین شکلی را پیدا می‌کند و ساده می‌شود:
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>();


یک نکته: امکان تزریق ApplicationDbContext به تمام کلاس‌های برنامه وجود دارد

همینقدر که ApplicationDbContext را به عنوان سرویسی در ConfigureServices تعریف کردیم، امکان تزریق آن در اجزای مختلف یک برنامه‌ی ASP.NET Core نیز وجود دارد:
using System.Linq;
using Core1RtmEmptyTest.Entities;
using Microsoft.AspNetCore.Mvc;

namespace Core1RtmEmptyTest.Controllers
{
    public class TestDBController : Controller
    {
        private readonly ApplicationDbContext _ctx;

        public TestDBController(ApplicationDbContext ctx)
        {
            _ctx = ctx;
        }

        public IActionResult Index()
        {
            var name = _ctx.Persons.First().FirstName;
            return Json(new { firstName = name });
        }
    }
}
در اینجا نحوه‌ی تزریق DB Context برنامه را به یک کنترلر مشاهده می‌کنید. البته هرچند تزریق یک کلاس مشخص به این شکل، تزریق وابستگی‌ها نام ندارد و هنوز این کنترلر دقیقا وابسته‌است به پیاده سازی خاص کلاس ApplicationDbContext، اما ... در کل امکان آن هست.

در این حالت پس از اجرای برنامه، خطای ذیل را مشاهده خواهیم کرد:


علت اینجا است که هنوز این بانک اطلاعاتی ایجاد نشده‌است و همچنین ساختار جداول را به آن منتقل نکرده‌ایم که این موارد را در قسمت‌های بعدی مرور خواهیم کرد.
مطالب دوره‌ها
متدهای async تقلبی
تا اینجا مشاهده کردیم که اگر یک چنین متد زمانبری را داشته باشیم که در آن عملیاتی طولانی انجام می‌شود،
class MyService
{
  public int CalculateXYZ()
  {
    // Tons of work to do in here!
    for (int i = 0; i != 10000000; ++i)
      ;
    return 42;
  }
}
برای نوشتن معادل async آن فقط کافی است که امضای متد را به async Task تغییر دهیم و سپس داخل آن از Task.Run استفاده کنیم:
class MyService
{
  public async Task<int> CalculateXYZAsync()
  {
    return await Task.Run(() =>
    {
      // Tons of work to do in here!
      for (int i = 0; i != 10000000; ++i)
        ;
      return 42;
    });
  }
}
و ... اگر از آن در یک کد UI استفاده کنیم، ترد آن‌را قفل نکرده و برنامه، پاسخگوی سایر درخواست‌های رسیده خواهد بود. اما ... به این روش اصطلاحا Fake Async گفته می‌شود؛ یا Async تقلبی!
کاری که در اینجا انجام شده، استفاده‌ی ناصحیح از Task.Run در حین طراحی یک متد و یک API است. عملیات انجام شده در آن واقعا غیرهمزمان نیست و در زمان انجام آن، باز هم ترد جدید اختصاص داده شده را تا پایان عملیات قفل می‌کند. اینجا است که باید بین CPU-bound operations و IO-bound operations تفاوت قائل شد. اگر Entity Framework 6 و یا کلاس WebClient و امثال آن، متدهایی Async را نیز ارائه داده‌اند، این‌ها به معنای واقعی کلمه، غیرهمزمان هستند و در آن‌ها کوچکترین CPU-bound operation ایی انجام نمی‌شود.
در حلقه‌ای که در مثال فوق در حال پردازش است و یا تمام اعمال انجام شده توسط CPU، از مرزهای سیستم عبور نمی‌کنیم. نه قرار است فایلی را ذخیره کنیم، نه با اینترنت سر و کار داشته باشیم و یا مثلا اطلاعاتی را از وب سرویسی دریافت کنیم و نه هیچگونه IO-bound operation خاصی قرار است صورت گیرد.
زمانیکه برنامه نویسی قرار است با API شما کار کند و به امضای async Task می‌رسد، فرضش بر این است که در این متد واقعا یک کار غیرهمزمان در حال انجام است. بنابراین جهت بالابردن کارآیی برنامه، این نسخه را نسبت به نمونه‌ی غیرهمزمان انتخاب می‌کند.
حال تصور کنید که استفاده کننده از این API یک برنامه‌ی دسکتاپ نیست، بلکه یک برنامه‌ی ASP.NET است. در اینجا Task.Run فراخوانی شده صرفا سبب خواهد شد عملیات مدنظر، بر روی یک ترد دیگر، نسبت به ترد اصلی اختصاص داده شده توسط ASP.NET برای فراخوانی و پردازش CalculateXYZAsync، صورت گیرد. این عملیات بهینه نیست. تمام پردازش‌های درخواست‌های ASP.NET در تردهای خاص خود انجام می‌شوند. وجود ترد دوم ایجاد شده توسط Task.Run در اینجا چه حاصلی را بجز سوئیچ بی‌جهت بین تردها و همچنین بالا بردن میزان کار Garbage collector دارد؟ در این حالت نه تنها سبب بالا بردن مقیاس پذیری سیستم نشده‌ایم، بلکه میزان کار Garbage collector و همچنین سوئیچ بین تردهای مختلف را در Thread pool برنامه به شدت افزایش داده‌ایم. همچنین یک چنین سیستمی برای تدارک تردهای بیشتر و مدیریت آن‌ها، مصرف حافظه‌ی بیشتری نیز خواهد داشت.


یک اصل مهم در طراحی کدهای Async
استفاده از Task.Run در پیاده سازی بدنه متدهای غیرهمزمان، یک code smell محسوب می‌شود.


چکار باید کرد؟
اگر در کدهای خود اعمال Async واقعی دارید که IO-bound هستند، از معادل‌های Async طراحی شده برای کار با آن‌ها، مانند متد SaveChangesAsync در EF، متد DownloadStringTaskAsync کلاس WebClient و یا متدهای جدید Async کلاس Stream برای خواندن و نوشتن اطلاعات استفاده کنید. در یک چنین حالتی ارائه متدهای async Task بسیار مفید بوده و در جهت بالابردن مقیاس پذیری سیستم بسیار مؤثر واقع خواهند شد.
اما اگر کدهای شما صرفا قرار است بر روی CPU اجرا شوند و تنها محاسباتی هستند، اجازه دهید مصرف کننده تصمیم بگیرد که آیا لازم است از Task.Run برای فراخوانی متد ارائه شده در کدهای خود استفاده کند یا خیر. اگر برنامه‌ی دسکتاپ است، این فراخوانی مفید بوده و سبب آزاد شدن ترد UI می‌شود. اگر برنامه‌ی وب است، به هیچ عنوان نیازی به Task.Run نبوده و فراخوانی متداول آن با توجه به اینکه درخواست‌های برنامه‌های ASP.NET در تردهای مجزایی اجرا می‌شوند، کفایت می‌کند.

به صورت خلاصه
از Task.Run در پیاده سازی بدنه متدهای API خود استفاده نکنید.
از Task.Run در صورت نیاز (مثلا در برنامه‌های دسکتاپ) در حین فراخوانی و استفاده از متدهای API ارائه شده استفاده نمائید:
 private async void MyButton_Click(object sender, EventArgs e)
{
  await Task.Run(() => myService.CalculateXYZ());
}
در این مثال از همان نسخه‌ی غیرهمزمان متد محاسباتی استفاده شده‌است و اینبار مصرف کننده است که تصمیم گرفته در حین فراخوانی و استفاده نهایی، برای آزاد سازی ترد UI از await Task.Run استفاده کند (یا خیر).

بنابراین نوشتن یک چنین کدهایی در پیاده سازی یک API غیرهمزمان
await Task.Run(() =>
{
   for (int i = 0; i != 10000000; ++i)
     ;
});
صرفا خود را گول زدن است. کل این عملیات بر روی CPU انجام شده و هیچگاه از مرزهای IO سیستم عبور نمی‌کند.

برای مطالعه بیشتر
Should I expose asynchronous wrappers for synchronous methods
مطالب
چک لیست ارتقاء به HTTPS مخصوص یک برنامه‌ی ASP.NET MVC 5x
پس از فعالسازی HTTPS بر روی سایت خود، در جهت بهبود امنیت برنامه‌های ASP.NET MVC 5.x، می‌توان درخواست کوکی‌های صرفا ارسال شده‌ی از طریق اتصال‌های HTTPS، اجبار به استفاده‌ی از آدرس‌های HTTPS و هدایت خودکار به آدرس‌های HTTPS را نیز فعالسازی کرد.


کوکی‌هایی که باید HTTPS only شوند

کوکی‌های پیش‌فرض برنامه‌های ASP.NET به صورت HTTP Only به سمت کلاینت ارسال می‌شوند. این کوکی‌ها توسط اسکریپت‌ها قابل خوانده شدن نیستند و به همین جهت یکی از راه‌های مقاومت بیشتر در برابر حملات XSS به شمار می‌روند. پس از ارتقاء به HTTPS، این کوکی‌ها را هم می‌توان HTTPs Only کرد تا فقط به کلاینت‌هایی که از طریق آدرس HTTPS سایت به آن وارد شده‌اند، ارائه شود:
1) کوکی آنتی‌فورجری توکن
 AntiForgeryConfig.RequireSsl = true;
محل تنظیم در متد Application_Start
2) کوکی‌های حالت Forms Authentication
<configuration>
  <system.web>
    <authentication mode="Forms">
      <forms requireSSL="true" cookieless="UseCookies"/>
    </authentication>
  </system.web>
</configuration>

3) تمام httpCookies
<configuration>
  <system.web>
    <httpCookies httpOnlyCookies="true" requireSSL="true" />
  </system.web>
</configuration>

4) کوکی‌های Role manager
<configuration>
  <system.web>
    <roleManager cookieRequireSSL="true" />
  </system.web>
</configuration>
محل تنظیم این سه مورد در فایل web.config است.

5) کوکی‌های OWIN Authentication و ASP.NET Identity 2.x
var options = new CookieAuthenticationOptions()
{
    CookieHttpOnly = true,
    CookieSecure = CookieSecureOption.Always,
    ExpireTimeSpan = TimeSpan.FromMinutes(10)
};


فعالسازی اجبار به استفاده‌ی از HTTPS

با استفاده از فیلتر RequireHttps، دسترسی به تمام اکشن متدهای برنامه تنها به صورت HTTPS میسر خواهد شد:
 filters.Add(new RequireHttpsAttribute(permanent: true));
محل تنظیم در متد RegisterGlobalFilters فایل Global.asax.cs، و یا در کلاس FilterConfig به صورت زیر:
using System.Web.Mvc;
namespace MyWebsite
{
    internal static class FilterConfig
    {
        internal static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            filters.Add(new RequireHttpsAttribute(permanent: true));
        }
    }
}
پارامتر permanent آن در چند به روز رسانی آخر MVC 5 به آن اضافه شده‌است (از نگارش 5.2.4 به بعد) و مخصوص موتورهای جستجو است؛ در جهت تصحیح خودکار آدرس‌های قدیمی سایت به آدرس‌های جدید.

همچنین در متد Application_BeginRequest نیز می‌توان بررسی کرد که آیا درخواست ارسالی یک درخواست HTTPS است یا خیر؟ اگر خیر، می‌توان کاربر را به صورت خودکار به نگارش HTTPS آن هدایت دائم کرد:
        protected void Application_BeginRequest(Object sender, EventArgs e)
        {
            if (!HttpContext.Current.Request.IsSecureConnection)
            {
                var builder = new UriBuilder
                {
                    Scheme = "https",
                    Host = Request.Url.Host,
                    // use the RawUrl since it works with URL Rewriting
                    Path = Request.RawUrl
                };
                Response.Status = "301 Moved Permanently";
                Response.AddHeader("Location", builder.ToString());
            }
        }
جهت محکم کاری این قسمت می‌توان دو تنظیم تکمیلی ذیل را نیز به فایل web.config اضافه کرد:
فعالسازی HSTS جهت اطلاع به مرورگر که این سایت تنها از طریق HTTPS قابل دسترسی است و تمام درخواست‌های HTTP را به صورت خودکار از طریق HTTPS انجام بده:
<httpProtocol>
      <customHeaders>
        <add name="Strict-Transport-Security" value="max-age=16070400; includeSubDomains" />
و همچنین اگر ماژول URL Rewite بر روی سرور نصب است، کار هدایت خودکار به آدرس‌های HTTPS را نیز می‌توان توسط آن مدیریت کرد:
<rewrite>
    <rules>        
        <rule name="Redirect to HTTPS" stopProcessing="true">
          <match url="(.*)" />
          <conditions>
            <add input="{HTTPS}" pattern="off" ignoreCase="true" />
            <add input="{HTTP_HOST}" negate="true" pattern="localhost" />
          </conditions>
          <action type="Redirect" url="https://{HTTP_HOST}/{R:1}" redirectType="Permanent" />
        </rule>


اصلاح تمام آدرس‌های مطلقی که توسط برنامه تولید می‌شوند

اگر در برنامه‌ی خود از Url.Action برای تولید آدرس‌ها استفاده می‌کنید، با ذکر پارامتر protocol آن، آدرس تولیدی آن بجای یک مسیر نسبی، یک مسیر مطلق خواهد بود. اگر پیشتر این پروتکل را به صورت دستی به http تنظیم کرده‌اید، روش صحیح آن به صورت زیر است که با آدرس جدید HTTPS سایت هم سازگار است:
 var fullBaseUrl = Url.Action(result: MVC.Home.Index(), protocol: this.Request.Url.Scheme);


اصلاح تمام آدرس‌هایی که پیشتر توسط برنامه تولید شده‌اند

یک نمونه آن در مطلب «به روز رسانی تمام فیلدهای رشته‌ای تمام جداول بانک اطلاعاتی توسط Entity framework 6.x» بحث شده‌است.


اصلاح فایل robots.txt و درج آدرس HTTPS جدید

اگر در فایل robots.txt سایت، آدرس مطلق Sitemap را به صورت HTTP درج کرده بودید، آن‌را به HTTPS تغییر دهید:
User-agent: *
Sitemap: https://www.dntips.ir/Sitemap
مطالب
ایجاد سرویس چندلایه‎ی WCF با Entity Framework در قالب پروژه - 5
پس از ایجاد متدها، نوبت به تغییرات App.Config می‎رسد. هرچند خود Visual Studio برای کلاس پیش‌گزیده‌ی خود تنظیماتی را در App.Config افزوده است ولی چنان‎چه در در خاطر دارید ما آن فایل‎ها را حذف کردیم و فایل‎های جدیدی به جای آن افزودیم. از این رو مراحل زیر را انجام دهید:
1- فایل App.Config را از Solution Explorer باز کنید.
2- به جای عبارت MyNewsWCFLibrary.Service1 در قسمت Service Name این عبارت را بنویسید: MyNewsWCFLibrary.MyNewsService
3- در قسمت BaseAddress عبارت Design_Time_Addresses را حذف کنید.
4- در قسمت BaseAddress شماره پورت را به 8080 تغییر دهید.
5- در قسمت BaseAddress به جای Service1 بنویسید: MyNewsService
6- در قسمت endpoint به جای عبارت MyNewsWCFLibrary.IService1 بنویسید: MyNewsWCFLibrary.IMyNewsService 
در پایان تگ Service در App.Config باید همانند کد زیر باشد:
   <services>
      <service name="MyNewsWCFLibrary.MyNewsService">
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:8080/MyNewsWCFLibrary/MyNewsService/" />
          </baseAddresses>
        </host>
        <!-- Service Endpoints -->
        <!-- Unless fully qualified, address is relative to base address supplied above -->
        <endpoint address="" binding="basicHttpBinding" contract="MyNewsWCFLibrary.IMyNewsService">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>
        <!-- Metadata Endpoints -->
        <!-- The Metadata Exchange endpoint is used by the service to describe itself to clients. -->
        <!-- This endpoint does not use a secure binding and should be secured or removed before deployment -->
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
تغییرات را ذخیره کنید و پروژه را اجرا کنید. باید پنجره‌ای شبیه به پنجره‌ی زیر نشان داده شود:

در صورت مشاهده پیام خطا، ویژوال استودیو را ببندید و این‌بار به صورت Run as administrator باز کنید.

برای نمونه روی متد AddCategory کلیک کنید. در پنجره نشان داده شده همانند شکل در برابر فیلد CatName مقداری وارد کنید و روی دکمه Invoke کلیک کنید. متد مورد نظر اجرا شده و مقداری که وارد کرده ایم در پایگاه داده‌ها ذخیره می‌شود. مقداری که در قسمت پایین دیده می‌شود خروجی متد است که در اینجا شناسه رکورد درج‌شده است.

بار دیگر برای مشاهده رکورد درج‌شده روی متد GetAllCategory کلیک کنید. به علت این‌که این متد ورودی ندارد در قسمت بالا چیزی نشان داده نمی‌شود. روی دکمه Invoke کلیک کنید. با پیغام خطای زیر روبه‌رو خواهید شد:

افزودن ویژگی Virtual به tblNews و tblCategory در بخش دوم  خواندید؛ باعث می‌شود که Entity Framework در هنگام اجرا کلاس‌هایی با عنوان "پروکسی‌های پویا" به کلاس‌های Address و Customer بیفزاید و بنابراین قابلیت Lazy Loading برای این کلاس‌ها در زمان اجرای برنامه فراهم می‌گردد. 

ولی با افزودن پروکسی‌های پویا به کلاس‌های ما، این کلاس‌ها قابلیت انتقال خود از طریق سرویس‌های WCF را از دست می‌دهند زیرا پروکسی‌های پویا به طور پیش‌گزیده قابلیت سریالایز و دیسریالایز شدن را ندارند!

خوشبختانه می‌توانیم این ویژگی را در کلاس DBContext غیرفعال کنیم. برای این منظور قالب سازنده‌ی آن یا MyNewsModel.Context.tt را از Solution Explorer باز کنید و کد زیر را در آن پیدا کنید:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
    public <#=code.Escape(container)#>()
        : base("name=<#=container.Name#>")
    {

سپس در ادامه‌ی آن کدغیرفعال‌کردن پروکسی پویا را به این شکل بنویسید:

<#=Accessibility.ForType(container)#> partial class <#=code.Escape(container)#> : DbContext
{
    public <#=code.Escape(container)#>()
        : base("name=<#=container.Name#>")
    {
      Configuration.ProxyCreationEnabled = false;

اکنون اگر فایل را ذخیره کنیم سپس فایل MyNewsModel.Context.cs را از Solution Explorer باز کنید؛ خواهید دید که این خط کد در جای خود قرارگرفته است.

بار دیگر پروژه را اجرا کنید روی متد GetAllCategory کلیک کنید. این بار اگر دکمه Invoke را بفشارید با همانند شکل زیر را خواهید دید:

در بخش ششم پیرامون ارتباط جدول‌های tblNews و tblCategory و نمایش محتویات وابسته جدول خبر به دسته و تنظیمات آن در t4 و کلاس Service

در بخش هفتم پیرامون میزبانی WCFLibrary در یک Web Application

و در بخش هشتم پیرامون ایجاد یک برنامه‌ی ویندوزی جهت استفاده از سرویس‌های WCF خواهم نوشت.