مطالب
نگاهی به تغییرات فایل Program.cs در نگارش‌های مختلف ASP.NET Core تا نگارش 6
در طی سال‌های قبل، نحوه‌ی آغاز برنامه‌های ASP.NET Core دچار تغییرات زیادی شده‌است که حداقل سه مورد مهم آن‌ها به صورت زیر است:
- استفاده از WebHost.CreateDefaultBuilder: این روش جهت تنظیم شروع به کار یک برنامه‌ی ASP.NET Core 2x مورد استفاده قرار می‌گرفت.
- استفاده از Host.CreateDefaultBuilder: روش پیش‌فرض آغاز برنامه‌های وب NET Core 3x. و NET 5x. که با معرفی generic host، امکان تهیه‌ی Worker services را میسر کردند.
- استفاده از WebApplication.CreateBuilder: روش جدید شروع به کار با برنامه‌های وب مبتنی بر NET 6.


استفاده از WebHost.CreateDefaultBuilder در ASP.NET Core 2.x

در نگارش اول ASP.NET Core، مفهومی به نام هاست پیش‌فرض (default host) وجود نداشت. یکی از مهم‌ترین نکات درنظر گرفته شده در طراحی ASP.NET Core، مفهوم «هزینه کردن به ازای احتیاج» است. یعنی اگر نیاز به قابلیتی نیست، نباید وجود داشته باشد. به این ترتیب یک قالب آغازین نباید به همراه تعداد زیادی بسته‌‌های NuGet و مقدار زیادی کد برای تنظیم باشد؛ زمانیکه واقعا قرار نیست از تمام آن‌ها استفاده شود. به همین جهت از نگارش 2، شروع به ساده‌کردن این قالب اولیه کردند و در این زمان، WebHost.CreateDefaultBuilder ارائه شد. این تنظیم به ظاهر ساده، کدهای پیش‌فرض مورد نیاز قابل توجهی را جهت ساخت ساده‌ی IWebHostBuilder و IWebHost به همراه دارد. در قالب به همراه آن، بین مفاهیم تنظیمات application و host تفاوت قائل شده‌اند؛ یعنی دو فایل Program.cs را برای تنظیمات application و فایل Startup.cs را برای ارائه‌ی تنظیمات هاست، تدارک دیدند.
در این طراحی، بین میدان دید Program و Startup تفاوت وجود دارد. هدف از Program، ارائه‌ی تنظیمات زیرساختی برنامه، مانند تنظیمات HTTP Server، یکپارچگی با IIS و امثال آن شد و عموما در طول عمر برنامه ثابت است. اما برای تغییر رفتار برنامه، بیشتر تغییرات و تنظیمات، به Startup محول شدند؛ مانند تنظیمات تزریق وابستگی‌ها، تنظیمات میان‌افزارها، مسیریابی‌ها و غیره.
public class Program
{
    public static void Main(string[] args)
    {
        BuildWebHost(args).Run();
    }

     public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .UseStartup<Startup>()
            .Build();
}
نمونه‌ا‌ی از فایل Program.cs برنامه‌های ASP.NET Core 2x را در اینجا ملاحظه می‌کنید این تنظیم ساده، کار خواندن فایل appsettings.json و برپایی تنظیمات برنامه، تنظیمات logging و همچنین تنظیمات یکپارچگی با Kestrel/IIS را در پشت صحنه انجام می‌دهد. همچنین ارجاعی را نیز به کلاس Startup برنامه دارد. متد UseStartup به صورت خودکار به دنبال متدهای ویژه‌ی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آن‌ها را فراخوانی می‌کند تا تنظیمات تزریق وابستگی‌ها، مسیریابی‌ها و میان‌افزارها صورت گیرند.


معرفی Generic Host در ASP.NET Core 3.x/5

ASP.NET Core 3.x به همراه تغییرات بزرگی در کدهای آغازین برنامه‌های ASP.NET Core بود. تا پیش از آن، امکانات پایه‌ی ASP.NET Core تنها برای مقاصد وب قابل استفاده بود. اما در نگارش‌های 3x، با ارائه‌ی یک هاست عمومی، امکان پشتیبانی از سایر برنامه‌ها، مانند worker services را نیز میسر کرد؛ برای مثال پیشتیبانی از کارهای طولانی پس زمینه، هاست سرویس‌های gRPC، هاست سرویس‌های ویندوز و غیره. هدف اصلی از این تغییرات، به اشتراک گذاری فریم‌ورک پایه‌ی ASP.NET Core که تنها برای برنامه‌های وب ساخته شده بود و به همراه امکاناتی مانند تنظیمات، ثبت وقایع، تزریق وابستگی‌ها و غیره بود، با سایر انواع برنامه‌های یاد شده است. برای رسیدن به این هدف، Web Host نگارش 2x، به Generic Host نگارش 3x تغییر کرد و سپس ASP.NET Core برفراز آن بنا شد. اینبار بجای IWebHostBuilder، نمونه‌ی جدید IHostBuilder را داریم:
public class Program
{
    public static void Main(string[] args)
    {
        CreateHostBuilder(args).Build().Run();
    }

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            }; 
    }
}
در اینجا نمونه‌ا‌ی از فایل Program.cs برنامه‌های ASP.NET Core 3x را ملاحظه می‌کنید. با توجه به عمومی بودن این روش، قسمت ConfigureWebHostDefaults آن، همان کار WebHost.CreateDefaultBuilder نگارش 2 ASP.NET Core را انجام می‌دهد.
ASP.NET Core 5 به همراه تغییرات مهمی در این زمینه نبود و تنها با تغییر target framework در فایل csproj و به روز رسانی بسته‌های نیوگت مرتبط، کار ارتقاء به نگارش جدید در آن صورت می‌گرفت.


معرفی  WebApplicationBuilder در ASP.NET Core 6x  

در دات نت 6، روش آغاز برنامه‌های وب بطور کامل تغییر کرده‌است. در تمام نگارش‌های پیشین ASP.NET Core، همواره شاهد دو فایل Program.cs و Startup.cs بودیم؛ اما در اینجا فقط یک فایل Program.cs بیشتر وجود ندارد. هر چند همانطور که در مطلب «ارتقاء فایل‌های آغازین برنامه‌های ASP.NET Core 5x به 6x» نیز عنوان شد، می‌توان همان سبک و سیاق پیشین را نیز برگرداند و از این لحاظ محدودیتی وجود ندارد.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddRazorPages();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
   app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
app.MapGet("/", () => "Hello World!");
app.MapRazorPages();
app.Run();
در اینجا نمونه‌ای از فایل Program.cs برنامه‌های دات نت 6 را مشاهده می‌کنید که تغییرات قابل ملاحظه‌ای داشته‌است:
- استفاده از top level statements
- استفاده از usingهای سراسری و سایر قابلیت‌های C# 10.0
- حذف کامل کلاس Startup؛ اینبار همه چیز فقط یک فایل است.

دیدگاهی را که در اینجا بکار گرفته‌اند شامل این موارد است و بیشتر تازه‌واردان را مدنظر دارند:
- برای شروع به کار، using statements اضافی هستند؛ پس حذف شده‌اند!
- برای شروع به کار، namespaces اضافی هستند؛ پس حذف شده‌اند!
- برای شروع به کار، Program.Main ... هم اضافی است!
- تنظیمات بین دو فایل Program.cs و Startup.cs تقیسم نشده‌اند و همه‌چیز یکجا است و این هم نیازی به توضیح اضافی به تازه‌واردان، ندارد.
- همچنین همانطور که عنوان شد، « ... متد UseStartup به صورت خودکار به دنبال متدهای ویژه‌ی ConfigureServices و Configure در کلاس آغازین برنامه گشته و آن‌ها را فراخوانی می‌کند تا تنظیمات تزریق وابستگی‌ها، مسیریابی‌ها و میان‌افزارها صورت گیرند ...»
نکته‌ی مهم این توضیح، این است که کلاس Startup، هیچ اینترفیسی را پیاده سازی نمی‌کند. یعنی نام این متدها، دقیقا باید به همین صورت باشند (بدون اینکه قرار دادی توسط یک اینترفیس برای آن‌ها وضع شده باشد) و ... چرا واقعا باید به این صورت باشد؟! به همین جهت این‌ها هم حذف شده‌اند!
- در اینجا WebApplication هم مشاهده می‌شود؛ اما آیا واقعا نیازی به آن است؟
پاسخ: خیر! WebApplication.CreateBuilder ای که در اینجا ملاحظه می‌کنید در حقیقت ساده شده‌ی قطعه کد زیر از کدهای ASP.NET Core 3x/5x است:
var hostBuilder = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services => 
    {
        services.AddRazorPages();
    })
    .ConfigureWebHostDefaults(webBuilder =>
    {
        webBuilder.Configure((ctx, app) => 
        {
            if (ctx.HostingEnvironment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapGet("/", () => "Hello World!");
                endpoints.MapRazorPages();
            });
        });
    }); 
hostBuilder.Build().Run();
که ... WebApplication.CreateBuilder بدون شک ساده‌تر از قطعه کد فوق است. همچنین پس از یک سطر زیر:
 var builder = WebApplication.CreateBuilder(args);
با استفاده از خاصیت builder.Configuration، می‌توان به تنظیمات برنامه دسترسی یافت و یا با استفاده از builder.Services می‌توان سرویس‌های جدیدی را ثبت کرد:
builder.Services.AddRazorPages();
builder.Services.AddSingleton<MyThingy>();
و یا با استفاده از خاصیت builder.Logging می‌توان تنظیمات ثبت وقایع برنامه را تغییر داد:
builder.Logging.AddFile();
همچنین در اینجا زمانیکه کار تنظیمات به پایان رسید، نمونه‌ای از WebApplication را به صورت زیر تولید کرده:
var app = builder.Build();
و اکنون می‌توان تنظیمات میان‌افزارها و یا مسیریابی‌ها را انجام داد:
app.UseStaticFiles();
app.MapRazorPages();
که در مقایسه با نگارش‌های قبلی، بسیار ساده‌تر شده‌، هرچند مرز بین این‌ها و ترتیب آن‌ها اندکی کم‌رنگ‌تر شده‌است.
نظرات مطالب
تغییرات رمزنگاری اطلاعات در NET Core.
یک نکته‌ی تکمیلی: ساده شدن روش کار با الگوریتم‌های هش از دات نت 5 به بعد

همانطور که در این مطلب مشاهده کردید، برای محاسبه‌ی هش SHA256، روش توصیه شده به صورت زیر درآمده‌است:
using System.Security.Cryptography;
byte[] data = default; // Some data
using (SHA256 hash = SHA256.Create())
{
    byte[] digest = hash.ComputeHash(data);
}
این مورد در دات نت 5 به صورت زیر خلاصه شده‌است:
using System.Security.Cryptography;
byte[] data = default; // Some data
byte[] digest = SHA256.HashData(data);
مزیت این روش نه فقط ساده شدن آن و یا حذف نیاز به using است، بلکه کاهش تعداد تخصیص‌های حافظه‌ی آن نیز هست. یک چنین API ای در دات نت 6 برای HMAC نیز تهیه شده‌است. همچنین برای نمونه الگوریتم هش کردن PBKDF2 در دات نت 6 به صورت زیر ساده شده‌است:
using System.Security.Cryptography;
byte[] salt = RandomNumberGenerator.GetBytes(32);
byte[] prk = Rfc2898DeriveBytes.Pbkdf2(
    userPassword,
    salt,
    iterations: 200_000,
    HashAlgorithmName.SHA256,
    outputLength: 32);
نظرات مطالب
مدیریت ساده‌تر امور Async (غیر همزمان) در نسخه‌ی بعدی زبان‌های دات نتی
- اتفاقا اخیرا مایکروسافت تمرکز کمتری روی زبان‌ها داشته و تمرکز بیشتر اون بر روی کتابخانه‌ها و افزونه‌های مرتبط با دات نت فریم ورک بوده مثل افزونه‌های برنامه نویسی موازی و بهبودهای زیادی که در زمینه برنامه نویسی چند ریسمانی به دات نت 4 اضافه شده. این مبحث ساده سازی async هم در جهت تکمیل این بحث است.
- هنوز هم با تمام این اضافات، حجم مستندات زبان سی شارپ در حد یک دانشنامه‌ی قطور مثل مستندات زبان CPP در نیامده است.
- اینکه مثلا زبان جاوا در چند سال اخیر درجا زده و مثلا در مورد LINQ یا قابلیت‌های پویای زبان یا همین مبحث ساده سازی Async کاری نکرده آیا به نظر شما دلیل مناسبی است برای کم کاری دیگران؟
نظرات مطالب
C# 12.0 - Primary Constructors
یک نکته‌ی تکمیلی: backing fields تولید شده‌ی در حالت سازنده‌های اولیه، readonly نیستند.

اگر به کدهای #Low-Level C مطلب فوق دقت کنید، به یک چنین تعاریفی می‌رسیم:
private string <FirstName>k__BackingField;
یعنی فیلدهای خصوصی متناظر با پارامترهای سازنده‌ی تعریف شده‌ی توسط کامپایلر در C# 12.0 از نوع readonly نیستند. بنابراین باید دقت داشت که این فیلدها قابل تغییر هستند و برخلاف رویه‌ی متداول تعریف فیلدهای متناظر با پارامترهای تزریق وابستگی‌ها (مانند مثال زیر)، readonly تعریف نشده‌اند:
public class AbcController : Controller
{
   private readonly IMyService _myService;

   public AbcController(IMyService myService) {
     _myService = myService;
   }
}
یعنی در حالت استفاده‌ی از سازنده‌های اولیه‌ی C# 12 می‌توان این فیلدها را تغییر داد که باید به این مورد دقت کافی را داشت. برای نمونه اگر مانند مثال زیر این فیلد را نال کنیم و تغییر دهیم، ممکن است در سایر قسمت‌های استفاده کننده‌ی از آن مشکل درست شود:
public class MyController(IMyService _myService) : ControllerBase
{
   [HttpGet]
   public IActionResult RandomNumber()
   {
     var number = _myService.RandomNumber();

     _myService = null;

     var number2 = MyRandom();
     return Ok(number + number2);            
   }

   public int MyRandom()
   {
     return _myService.RandomNumber();
   }
}
در این مثال ابتدا از myService_ استفاده شده، سپس نال شده (و با خطایی هم مواجه نمی‌شویم چون readonly نیست) و مجددا از آن استفاده شده که به علت نال بودن، دیگر چنین چیزی میسر نیست.
نظرات مطالب
تاریخچه‌ی نگارش‌های مختلف دات نت فریم ورک
ممنون. دات نت فریم ورک پایه و اساس محصولات جدید مایکروسافت است. به همین جهت توسعه‌ی آن برای آن‌ها خیلی اهمیت دارد.
WF برای مثال در شیرپوینت و بیزتاک سرور داره استفاده میشه. یا خیلی از قسمت‌هایی که به ASP.Net‌ داره اضافه میشه به نظر من با هدفگیری این محصولات است که خیرش داره به بقیه هم میرسه.
management studio اس کیوال سرورهای 2005 به بعد با دات نت نوشته شده.
و خیلی از مواردی که مایکروسافت از لحاظ امنیتی با آن‌ها مشکل داشت با کوچ به دات نت فریم ورک به شدت امن شده‌اند چون برای مثال buffer overflow attacks در دات نت معنی ندارد و اگر کد شما فقط از managed code تشکیل شده باشد، کاملا کنترل شده است. به همین جهت اگر به لیست تعداد حملات صورت گرفته به اس کیوال سرورهای 2005 به بعد مراجعه کنید، کاهش محسوسی رو می‌تونید ملاحظه کنید.