مطالب
امکان تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه
تغییر پویای رشته‌ی اتصالی به بانک اطلاعاتی در نگارش‌های پیشین EF، مشکل بودند که نمونه‌هایی از آن را پیشتر در مطالب زیر مشاهده کرده‌اید:
- «تنظیم رشته اتصالی Entity Framework به بانک اطلاعاتی به وسیله کد»
- «استفاده از چندین بانک اطلاعاتی به صورت همزمان در EF Code First»

اما EF Core نه تنها این مشکل را پوشش را داده‌است، بلکه امکان تزریق وابستگی‌ها و استفاده‌ی از سرویس‌های مختلف را نیز در این حین، پیش بینی کرده‌است که در ادامه جزئیات آن‌را مرور می‌کنیم.


نیاز به تغییر رشته‌ی اتصالی به بانک اطلاعاتی در زمان اجرا

دلایل نیاز به امکان تغییر رشته‌ی اتصالی در زمان اجرا شامل موارد زیر هستند:
- در برنامه‌هایی کمی پیچیده‌تر و سابقه دار، ممکن است عملیات تجاری یکسال را در بانک اطلاعاتی سال 98 و دیگری را در بانک اطلاعاتی سال 99 ثبت کنید. در این حالت کاربران باید بتوانند در زمان اجرا به هر بانک اطلاعاتی که پیشتر با آن کار کرده‌اند، متصل شده و از آن استفاده کنند.
- یکی از روش‌های پیاده سازی برنامه‌های چند مستاجری، داشتن یک بانک اطلاعاتی مجزا، به ازای هر مستاجر است. در این حالت نیز تک برنامه‌ی ما باید بتواند بر اساس Id مشتری، بانک اطلاعاتی متناظری را در زمان اجرا انتخاب کند.
- نیاز به داشتن چندین context در برنامه و کار با بانک‌های اطلاعاتی متفاوت در زمان اجرا؛ مانند کار با SQL Server، اوراکل و یا SQLite


روش تغییر رشته‌ی اتصالی به بانک اطلاعاتی در EF Core در زمان اجرای برنامه

اگر به روش ثبت متداول سرویس DbContext برنامه و پروایدر آن دقت کنیم:
services.AddDbContext<ApplicationDbContext>(options =>
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")
));
یک action delegate قابل مشاهده‌است. کار این اکشن، تنظیم پروایدر و تمام نیازهای یک رشته‌ی اتصالی به بانک اطلاعاتی، جهت شروع به کار با Context برنامه است. نکته‌ی مهمی که در اینجا وجود دارد، فراخوانی هرباره‌ی این action، به ازای هر اتصال تشکیل شده‌است. یعنی کدهای داخل این action delegate کش نمی‌شوند و همین مساله امکان تغییر پویای آن‌ها را میسر می‌کند.

یک نکته: چون این اطلاعات کش نمی‌شوند، اگر رشته‌ی اتصالی شما ثابت است (و نیازی به تغییر آن در زمان اجرای برنامه نیست)، محل تامین آن‌را به پیش از سطر services.AddDbContext انتقال دهید و فقط نتیجه‌ی محاسبه شده‌ی نهایی را استفاده کنید تا کارآیی برنامه افزایش یابد؛ در غیراینصورت فراخوانی Configuration.GetConnectionString مدام تکرار خواهد شد.


دریافت یک قالب قابل تغییر از تنظیمات برنامه و تغییر آن با هدرهای درخواست رسیده‌ی به آن

فرض کنید قالب رشته‌ی اتصالی برنامه در فایل appsettings.json به صورت زیر است:
"ConnectionStrings": {
    "ConnectionTemplate": "Data Source=.;Initial Catalog={db_Name};Integrated Security=True",
}
و db_Name آن قرار است برای مثال از یک query string، سشن، کوکی و یا فیلد خاصی در هدر HTTP رسیده تامین شود. برای مثال سال مالی انتخابی و یا شماره مستاجر انتخابی به صورت یک فیلد خاص HTTP به سمت برنامه ارسال می‌شوند.
بنابراین اکنون نیاز است به ازای هر درخواست رسیده بتوان به سرویس IHttpContextAccessor و شیء HttpContext.Request جاری دسترسی یافت و سپس از هدرهای رسیده، برای مثال هدر ویژه‌ی tenantId و یا year را پردازش کرد؛ اما در تعریف services.AddDbContext فوق چگونه می‌توان اینکار را انجام داد؟
خوشبختانه متد services.AddDbContext، دارای یک overload دیگر نیز هست که امکان دسترسی به تمام سرویس‌های جاری سیستم را میسر می‌کند:
services.AddDbContext<ApplicationDbContext>((serviceProvider, dbContextBuilder) =>
{
   var connectionStringTemplate = Configuration.GetConnectionString("ConnectionTemplate");
   var httpContextAccessor = serviceProvider.GetRequiredService<IHttpContextAccessor>();
   var dbName = httpContextAccessor.HttpContext.Request.Headers["tenantId"].First();
   var connectionString = connectionStringTemplate.Replace("{db_Name}", dbName);
   dbContextBuilder.UseSqlServer(connectionString);
});
همانطور که مشاهده می‌کنید، overload دوم متد services.AddDbContext، امکان ارسال serviceProvider را نیز به این action delegate دارد. پس از آن می‌توان توسط متد GetRequiredService آن به هر سرویس مدنظری که در سیستم ثبت شده‌است، دسترسی یافت و برای مثال در اینجا فیلد هدر سفارشی tenantId را از آن استخراج نمود و در قالب رشته‌ی اتصالی به بانک اطلاعاتی، در زمان اجرا به صورت پویایی جایگزین کرد.
همچنین در صورت نیاز می‌توان UseSqlServer آن‌را نیز در این action delegate به هر پروایدر دیگری در زمان اجرا تغییر داد و از این لحاظ محدودیتی وجود ندارد.

یک نکته: البته برنامه نباید هر tenantId ای را پردازش کند و این خودش می‌تواند تبدیل به یک نقیصه‌ی امنیتی شود. به همین جهت برای مثال می‌توان tenantId را در یک JWT قرار داد و در حین تعیین اعتبار آن و کاربر جاری، این مقدار را نیز بررسی کرد.
نظرات مطالب
تکمیل کلاس DelegateCommand
درود بر شما؛ در راستای فرمایشات شما، ویدیوهای زیر میتونه برای دوستان جالب باشه:

http://channel9.msdn.com/posts/NYC-DevReady-MVVM-Session-2-of-5-Programming-with-MVVM-Part-1
http://channel9.msdn.com/posts/NYC-DevReady-MVVM-Session-3-of-5-Programming-with-MVVM-Part-2
تو بخش اول مفصلا راجع به DelegateCommand بحث شده؛ پاینده باشید.
نظرات مطالب
ویدیوهای رایگان آموزش jQuery
برای تمام ویدیوها فوق mirror‌ تهیه کرده‌ام:
https://www.dntips.ir/2008/12/mirror.html

و mirror برای ویدیوهای جدید این مجموعه:
Day 14: Implementing Your First Plugin
http://rapidshare.com/files/181363327/v3bg115310.flv

Day 15: Building a jQuery Style-Switcher: Part 1
http://rapidshare.com/files/181363189/n62ldrzuyo.flv

Day 15: Building a jQuery Style-Switcher: Part 2
http://rapidshare.com/files/181363345/qkrwv8jzsf.flv
مطالب
تنظیمات کش توزیع شده‌ی مبتنی بر SQL Server در ASP.NET Core
ASP.NET Core به همراه زیر ساختی‌است جهت خارج کردن امکانات Caching درون حافظه‌ای آن از سرور جاری و انتقال آن به سرورهای دیگر جهت کاهش بار سرور و برنامه. این کش توزیع شده را می‌توان به عنوان زیرساختی برای مدیریت سشن‌ها، مدیریت اطلاعات کش و همچنین مدیریت کوکی‌های حجیم ASP.NET Core Identity نیز بکار گرفت. برای مثال بجای ارسال یک کوکی حجیم بالای 5 کیلوبایت به کلاینت، فقط ID رمزنگاری شده‌ی آن‌را ارسال کرد و اصل کوکی را در داخل دیتابیس ذخیره و بازیابی نمود. این مساله هم مقیاس پذیری برنامه را افزایش خواهد داد و هم امنیت آن‌را با عدم ارسال اصل محتوای کوکی به سمت کلاینت‌ها و یا ذخیره سازی اطلاعات سشن‌ها در بانک اطلاعاتی، مشکلات راه اندازی مجدد برنامه را به طور کامل برطرف می‌کنند و در این حالت بازیابی Application pool و یا کرش برنامه و یا ری استارت شدن کل سرور، سبب از بین رفتن سشن‌های کاربران نخواهند شد. بنابراین آشنایی با نحوه‌ی راه اندازی این امکانات، حداقل از بعد امنیتی بسیار مفید هستند؛ حتی اگر سرور ذخیره کننده‌ی این اطلاعات، همان سرور و بانک اطلاعاتی اصلی برنامه باشند.


پیشنیازهای کار با کش توزیع شده‌ی مبتنی بر SQL Server

برای کار با کش توزیع شده‌ی با قابلیت ذخیره سازی در یک بانک اطلاعاتی SQL Server، نیاز است دو بسته‌ی ذیل را به فایل project.json برنامه اضافه کرد:
{
    "dependencies": {
        "Microsoft.Extensions.Caching.SqlServer": "1.1.0"
    },
    "tools": {
        "Microsoft.Extensions.Caching.SqlConfig.Tools": "1.1.0-preview4-final"
    }
}
وابستگی که در قسمت dependencies ذکر شده‌است، کلاس‌های اصلی کار با کش توزیع شده را به برنامه اضافه می‌کند. ذکر وابستگی قسمت tools، اختیاری است و کار آن، ایجاد جدول مورد نیاز برای ذخیره سازی اطلاعات، در یک بانک اطلاعاتی SQL Server می‌باشد.


ایجاد جدول ذخیره سازی اطلاعات کش توزیع شده به کمک ابزار sql-cache

پس از افزودن و بازیابی ارجاعات فوق، با استفاده از خط فرمان، به پوشه‌ی جاری برنامه وارد شده و دستور ذیل را صادر کنید:
 dotnet sql-cache create "Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=sql_cache;Integrated Security=True;" "dbo" "AppSqlCache"
توضیحات:
- در اینجا می‌توان هر نوع رشته‌ی اتصالی معتبری را به انواع و اقسام بانک‌های SQL Server ذکر کرد. برای نمونه در مثال فوق این رشته‌ی اتصالی به یک بانک اطلاعاتی از پیش ایجاد شده‌ی LocalDB اشاره می‌کند. نام دلخواه این بانک اطلاعاتی در اینجا sql_cache ذکر گردیده و نام دلخواه جدولی که قرار است این اطلاعات را ثبت کند AppSqlCache تنظیم شده‌است و dbo، نام اسکیمای جدول است:


در اینجا تصویر ساختار جدولی را که توسط ابزار dotnet sql-cache ایجاد شده‌است، مشاهده می‌کنید. اگر خواستید این جدول را خودتان دستی ایجاد کنید، یک چنین کوئری را باید بر روی دیتابیس مدنظرتان اجرا نمائید:
CREATE TABLE AppSqlCache (
    Id                         NVARCHAR (449)  COLLATE SQL_Latin1_General_CP1_CS_AS NOT NULL,
    Value                      VARBINARY (MAX) NOT NULL,
    ExpiresAtTime              DATETIMEOFFSET  NOT NULL,
    SlidingExpirationInSeconds BIGINT          NULL,
    AbsoluteExpiration         DATETIMEOFFSET  NULL,
    CONSTRAINT pk_Id PRIMARY KEY (Id)
);

CREATE NONCLUSTERED INDEX Index_ExpiresAtTime
    ON AppSqlCache(ExpiresAtTime);


ایجاد جدول ذخیره سازی اطلاعات کش توزیع شده به کمک ابزار Migrations در EF Core

زیر ساخت کش توزیع شده‌ی مبتنی بر SQL Server هیچگونه وابستگی به EF Core ندارد و تمام اجزای آن توسط Async ADO.NET نوشته شده‌اند. اما اگر خواستید قسمت ایجاد جدول مورد نیاز آن‌را به ابزار Migrations در EF Core واگذار کنید، روش کار به صورت زیر است:
- ابتدا یک کلاس دلخواه جدید را با محتوای ذیل ایجاد کنید:
    public class AppSqlCache
    {
        public string Id { get; set; }
        public byte[] Value { get; set; }
        public DateTimeOffset ExpiresAtTime { get; set; }
        public long? SlidingExpirationInSeconds { get; set; }
        public DateTimeOffset? AbsoluteExpiration { get; set; }
    }
- سپس تنظیمات ایجاد جدول متناظر با آن را به نحو ذیل تنظیم نمائید:
    public class MyDBDataContext : DbContext
    {
        public virtual DbSet<AppSqlCache> AppSqlCache { get; set; }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<AppSqlCache>(entity =>
            {
                entity.ToTable(name: "AppSqlCache", schema: "dbo");
                entity.HasIndex(e => e.ExpiresAtTime).HasName("Index_ExpiresAtTime");
                entity.Property(e => e.Id).HasMaxLength(449);
                entity.Property(e => e.Value).IsRequired();
            });
        }
    }
به این ترتیب این جدول جدید به صورت خودکار در کنار سایر جداول برنامه ایجاد خواهند شد.
البته این مورد به شرطی است که بخواهید از یک دیتابیس، هم برای برنامه و هم برای ذخیره سازی اطلاعات کش استفاده کنید.


معرفی تنظیمات رشته‌ی اتصالی و نام جدول ذخیره سازی اطلاعات کش به برنامه

پس از ایجاد جدول مورد نیاز جهت ذخیره سازی اطلاعات کش، اکنون نیاز است این اطلاعات را به برنامه معرفی کرد. برای این منظور به کلاس آغازین برنامه مراجعه کرده و متد الحاقی AddDistributedSqlServerCache را بر روی مجموعه‌ی سرویس‌های موجود فراخوانی کنید؛ تا سرویس‌های این کش توزیع شده نیز به برنامه معرفی شوند:
public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedSqlServerCache(options =>
    {
        options.ConnectionString = @"Data Source=(localdb)\MSSQLLocalDB;Initial Catalog=sql_cache;Integrated Security=True;";
        options.SchemaName = "dbo";
        options.TableName = "AppSqlCache";
    });
باتوجه به توزیع شده بودن این کش، هیچ الزامی ندارد که ConnectionString ذکر شده‌ی در اینجا با رشته‌ی اتصالی مورد استفاده‌ی توسط EF Core یکی باشد (هرچند مشکلی هم ندارد).


آزمایش کش توزیع شده‌ی تنظیمی با فعال سازی سشن‌ها

سشن‌ها را همانند نکات ذکر شده‌ی در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 16 - کار با Sessions» فعال کنید و سپس مقداری را در آن بنویسید:
public IActionResult Index()
{
   HttpContext.Session.SetString("User", "VahidN");
   return Json(true);
}

public IActionResult About()
{
   var userContent = HttpContext.Session.GetString("User");
   return Json(userContent);
}
اکنون از جدول AppSqlCache کوئری بگیرید:


همانطور که مشاهده می‌کنید، سیستم سشن اینبار بجای حافظه، به صورت خودکار از جدول بانک اطلاعاتی SQL Server تنظیم شده‌، برای ذخیره سازی اطلاعات خود استفاده کرده‌است.


کار با کش توزیع شده از طریق برنامه نویسی

همانطور که در مقدمه‌ی بحث نیز عنوان شد، استفاده‌ی از زیر ساخت کش توزیع شده منحصر به استفاده‌ی از آن جهت ذخیره سازی اطلاعات سشن‌ها نیست و از آن می‌توان جهت انواع و اقسام سناریوهای مختلف مورد نیاز استفاده کرد. در این حالت روش دسترسی به این زیر ساخت، از طریق اینترفیس IDistributedCache است. زمانیکه متد AddDistributedSqlServerCache را فراخوانی می‌کنیم، در حقیقت کار ثبت یک چنین سرویسی به صورت خودکار انجام خواهد شد:
 services.Add(ServiceDescriptor.Singleton<IDistributedCache, SqlServerCache>());
به عبارتی کلاس SqlServerCache به صورت singleton به مجموعه‌ی سرویس‌های برنامه اضافه شده‌است و برای دسترسی به آن تنها کافی است اینترفیس IDistributedCache را به کنترلرها و یا سرویس‌های برنامه تزریق و از امکانات آن استفاده کنیم.

در اینجا یک نمونه از این تزریق وابستگی و سپس استفاده‌ی از متدهای Set و Get اینترفیس IDistributedCache را مشاهده می‌کنید:
using System;
using System.Text;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Distributed;
 
namespace Core1RtmEmptyTest.Controllers
{
    public class CacheTestController : Controller
    {
        readonly IDistributedCache _cache;
        public CacheTestController(IDistributedCache cache)
        {
            _cache = cache;
        }
 
        public IActionResult SetCacheData()
        {
            var time = DateTime.Now.ToLocalTime().ToString();
            var cacheOptions = new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTime.Now.AddYears(1)
 
            };
            _cache.Set("Time", Encoding.UTF8.GetBytes(time), cacheOptions);
            return View();
        }
 
        public IActionResult GetCacheData()
        {
            var time = Encoding.UTF8.GetString(_cache.Get("Time"));
            ViewBag.data = time;
            return View();
        }
 
        public bool RemoveCacheData()
        {
            _cache.Remove("Time");
            return true;
        }
    }
}
در ابتدای بحث که ساختار جدول ذخیره سازی اطلاعات کش را بررسی کردیم، فیلد value آن یک چنین نوعی را دارد:
  Value  VARBINARY (MAX) NOT NULL,
که در سمت کدهای دات نتی، به شکل آرایه‌ای از بایت‌ها قابل بیان است.
  public byte[] Value { get; set; }
به همین جهت متد Set آن مقدار مدنظر را به صورت آرایه‌ای از بایت‌ها قبول می‌کند.
در این حالت اگر برنامه را اجرا و مسیر http://localhost:7742/CacheTest/SetCacheData را فراخوانی کنیم، اطلاعات ذخیره شده‌ی با کلید Test را می‌توان در بانک اطلاعاتی مشاهده کرد:



Tag helper مخصوص کش توزیع شده

در ASP.NET Core، می‌توان از یک Tag Helper جدید به نام distributed-cache برای کش سمت سرور توزیع شده‌ی محتوای قسمتی از یک View به نحو ذیل استفاده کرد:
<distributed-cache name="MyCacheItem2" expires-sliding="TimeSpan.FromMinutes(30)">
    <p>From distributed-cache</p>
    @DateTime.Now.ToString()
</distributed-cache>
که اطلاعات آن در بانک اطلاعاتی به نحو ذیل ذخیره می‌شود:


در اینجا name به صورت هش شده به صورت کلید کش مورد استفاده قرار می‌گیرد. سپس محتوای تگ distributed-cache رندر شده، تبدیل به آرایه‌ای از بایت‌ها گردیده و در بانک اطلاعاتی ذخیره می‌گردد.
ذکر name در اینجا اجباری است و باید دقت داشت که چون به عنوان کلید بازیابی کش مورد استفاده قرار خواهد گرفت، نباید به اشتباه در قسمت‌های دیگر برنامه با همین نام وارد شود. در غیر اینصورت دو قسمتی که name یکسانی داشته باشند، یک محتوا را نمایش خواهند داد.
پاسخ به بازخورد‌های پروژه‌ها
خطای data binding
با سلام 
موارد پیش نیاز از قبل نصب شده بود و آخرین نگارش مورد استفاده قرار گرفته بود.
دیتیل خطا بصورت زیر است.
با تشکر
System.Windows.Markup.XamlParseException occurred
  HResult=-2146233087
  Message=The invocation of the constructor on type 'DemosBrowser.ViewModels.BrowserViewModel' that matches the specified binding constraints threw an exception.
  Source=PresentationFramework
  LineNumber=0
  LinePosition=0
  StackTrace:
       at System.Windows.Markup.WpfXamlLoader.Load(XamlReader xamlReader, IXamlObjectWriterFactory writerFactory, Boolean skipJournaledProperties, Object rootObject, XamlObjectWriterSettings settings, Uri baseUri)
       at System.Windows.Markup.WpfXamlLoader.LoadBaml(XamlReader xamlReader, Boolean skipJournaledProperties, Object rootObject, XamlAccessLevel accessLevel, Uri baseUri)
       at System.Windows.Markup.XamlReader.LoadBaml(Stream stream, ParserContext parserContext, Object parent, Boolean closeStream)
       at System.Windows.Application.LoadComponent(Object component, Uri resourceLocator)
       at DemosBrowser.Views.Browser.InitializeComponent() in ...\pdfreport-27009\Samples\DemosBrowser\Views\Browser.xaml:line 1
       at DemosBrowser.Views.Browser..ctor() in ...\pdfreport-27009\Samples\DemosBrowser\Views\Browser.xaml.cs:line 8
  InnerException: System.Runtime.InteropServices.COMException
       HResult=-2147221164
       Message=Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG))
       Source=System.Windows.Forms
       ErrorCode=-2147221164
       StackTrace:
            at System.Windows.Forms.UnsafeNativeMethods.CoCreateInstance(Guid& clsid, Object punkOuter, Int32 context, Guid& iid)
            at System.Windows.Forms.AxHost.CreateWithoutLicense(Guid clsid)
            at System.Windows.Forms.AxHost.CreateWithLicense(String license, Guid clsid)
            at System.Windows.Forms.AxHost.CreateInstanceCore(Guid clsid)
            at System.Windows.Forms.AxHost.CreateInstance()
            at System.Windows.Forms.AxHost.GetOcxCreate()
            at System.Windows.Forms.AxHost.TransitionUpTo(Int32 state)
            at System.Windows.Forms.AxHost.CreateHandle()
            at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
            at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
            at System.Windows.Forms.AxHost.EndInit()
            at DemosBrowser.Toolkit.AcrobatReader.AcroPdf.InitializeComponent() in ...27009\Samples\DemosBrowser\Toolkit\AcrobatReader\AcroPdf.Designer.cs:line 53
            at DemosBrowser.Toolkit.AcrobatReader.AcroPdf..ctor(String fileName) in ...\pdfreport-27009\Samples\DemosBrowser\Toolkit\AcrobatReader\AcroPdf.cs:line 10
            at DemosBrowser.ViewModels.BrowserViewModel.setupAcrobatReader() in ...\pdfreport-27009\Samples\DemosBrowser\ViewModels\BrowserViewModel.cs:line 253
            at DemosBrowser.ViewModels.BrowserViewModel..ctor() in ...\pdfreport-27009\Samples\DemosBrowser\ViewModels\BrowserViewModel.cs:line 36
       InnerException: 
نظرات مطالب
بهبود کارآیی حلقه‌های foreach در دات نت 7
یک نکته‌ی تکمیلی: آشنایی با مفهوم «C# Lowering»


در این مطلب، جهت بررسی درک علت یکسان بودن کارآیی حلقه‌هایی که از دیدگاه ما یکی نیستند، از قابلیت نمایش #Low-level C استفاده شد که نام اصلی آن «C# Lowering» است. Lowering به معنای ترجمه‌ی امکانات سطح بالای یک زبان به امکانات سطح پایین آن است. یعنی حاصل عملیات صورت گرفته نیز باز به همان زبان اولیه است که نمونه‌ی آن، تبدیل یک حلقه‌ی foreach سطح بالا به نمونه‌ی سطح پایینی است که توسط NET Runtime. بهتر درک شده و ساده‌تر اجرا می‌شود.

مزایای Lowering
- بهبود کارآیی برنامه: برای مثال یکی از کارهایی که در این بین عموما انجام می‌شود «Loop unrolling» است. یعنی یک حلقه به چندین حلقه‌ی کوچکتر تقسیم می‌شود تا سربار instructions کنترلی حلقه کاهش پیدا کنند.
- طراحی ساده‌تر زبان: اینکار به تیم طراحی زبان امکان نوشتن کدهای اضافه‌تری را می‌دهد که کار برنامه نویس‌ها را کمتر می‌کند. برای مثال یک record واقعا چیزی نیست بجز یک کلاس پیاده سازی کننده‌ی IEquatable به صورت خودکار و در پشت صحنه.

Lowering چه زمانی رخ می‌دهد؟
Lowering جزئی از عملیات صورت گرفته‌ی در حین کامپایل است. زمانیکه دستور dotnet build را صادر می‌کنیم، ابتدا semantics & syntax analysis صورت می‌گیرد تا اگر برای مثال خطای دستوری وجود دارد، مشخص شود. سپس کدها به CIL یا Common intermediate language تبدیل می‌شوند. در حین این قسمت است که عملیات lowering نیز انجام می‌شود.
اگر علاقمند به مشاهده‌ی این کد #C ثانویه‌ی تولید شده‌ی توسط کامپایلر هستید، می‌توان از ابزار https://sharplab.io نیز استفاده کرد. برای مثال در سمت چپ آن کدهای زیر را قرار دهید:
using System;
using System.Collections.Generic;

var list = new List<int> { 1, 2 };

foreach(var item in list)
    Console.Write(item);
سپس در سمت راست آن، گزینه‌ی #Results C را انتخاب کنید تا بتوانید نمونه‌ی معادل تبدیل شده‌ی توسط کامپایلر را مشاهده نمائید.
اشتراک‌ها
کتاب رایگان JWT Handbook

Ever wondered how JWT came to be and what problems it was designed to tackle? Are you curious about the plethora of algorithms available for signing and encrypting JWTs? Or are you interested in getting up-to-speed with JWTs as soon as possible? Then this handbook is for you. 

کتاب رایگان JWT Handbook
اشتراک‌ها
بررسی imageهای مختلف NET. در Docker

.NET and .NET Core (and Windows!) have been getting better and better with Docker. I run Docker for Windows as it supports both Linux Containers and Windows Containers. They have both a Stable and Edge channel. The Edge (Beta) channel is regularly updated and, as a rule, gets better and better in the year I've been running it. 

بررسی imageهای مختلف NET. در Docker
اشتراک‌ها
هر برنامه نویس یک خود آموخته است

 There are many ways to become a programmer beside getting a computer science degree. If you’re on that less conventional path, you may be wondering what you should do to catch up to people who do have a degree. How can you compete with someone who spent many years in a classwork learning about computers and programming? 

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