‫۲ سال و ۶ ماه قبل، پنجشنبه ۱۲ اسفند ۱۴۰۰، ساعت ۱۳:۳۵
یک نکته‌ی تکمیلی: دسترسی به کلاس Program از نوع internal دات نت 6 در یک پروژه‌ی دیگر

در مطلب فوق چنین قطعه کدی را داریم:
public class CustomWebApplicationFactory : WebApplicationFactory<Program>
اگر از top level programs دات نت 6 استفاده کنیم، اینبار کلاس Program دیگر public نیست و internal است. به همین جهت نمی‌توان از کلاس Program در یک اسمبلی دیگر استفاده کرد؛ مگر اینکه در فایل csproj پروژه‌ی اصلی، تنظیم زیر را اضافه کنیم. در اینجا Include به نام پروژه‌ی Test (و فضای نام اصلی اسمبلی آن) اشاره می‌کند. همچنین چون Program از نوع internal است، اینبار کلاس فکتوری تهیه شده نیز باید internal تعریف شود:
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <InternalsVisibleTo Include="ProjName.Tests" />
  </ItemGroup>
</Project>
‫۲ سال و ۶ ماه قبل، دوشنبه ۹ اسفند ۱۴۰۰، ساعت ۱۳:۳۴
یک نکته‌ی تکمیلی: امکان اجباری کردن وجود قسمتی در تنظیمات برنامه در دات نت 6

در مطلب فوق، نمونه‌ی استفاده از متد Configuration.GetSection را مشاهده می‌کنید؛ مانند: Configuration.GetSection("Auth:Users"). در دات نت 6 متد جدیدی به نام GetRequiredSection نیز بجای GetSection معرفی شده‌است که در صورت وجود نداشتن چنین قسمتی در فایل تنظیمات برنامه، یک استثناء را در ابتدای کار و شروع آن، صادر می‌کند.
‫۲ سال و ۶ ماه قبل، شنبه ۷ اسفند ۱۴۰۰، ساعت ۱۴:۳۹
یک نکته‌ی تکمیلی: ساده شدن تعریف Lambda Expressions در C# 10.0


تا پیش از C# 10.0 جهت تعریف Lambda Expressions نیاز بود تا کمی بیشتر کد نوشت. برای مثال:
Func<string, int> parse = (string s) => int.Parse(s);
در یک چنین تعاریفی، ذکر صریح Func و Action ضروری است.

با ارائه‌ی C# 10.0، مفهومی به نام natural lambda expression ارائه شده‌است که در آن کامپایلر سعی می‌کند تا نوع این Action و Funcها را بر اساس تعریف lambda expression، تشخیص دهد. در این حالت قطعه کد فوق، به صورت زیر خلاصه می‌شود:
 var parse = (string s) => int.Parse(s);
البته باید دقت داشت که این type inferring، بر اساس ذکر دقیق نوع‌های سمت راست عبارت فوق میسر شده؛ وگرنه قطعه کد زیر، با خطای «The delegate type could not be inferred» کامپایل نمی‌شود؛ چون نوع پارامتر lambda مشخص نشده‌است:
var upper = (s) => s.ToUpperInvariant();

همچنین در C# 10.0 می‌توان این نوع پیش‌فرض تشخیص داده شده‌ی توسط کامپایلر را نیز صراحتا مشخص کرد و تغییر داد:
var createException = (bool b) => b ? new ArgumentNullException() : new DivideByZeroException();
قطعه کد فوق نیز با خطای «The delegate type could not be inferred» کامپایل نمی‌شود؛ چون دقیقا مشخص نیست که چه نوع خروجی را باید مدنظر قرار داد. در این حالت می‌توان این نوع را به صورت زیر، پیش از تعریف Lambda Expression قرار داد و مشخص کرد:
var createException = Exception (bool b) => b ? new ArgumentNullException() : new DivideByZeroException();
در این حالت نوع خروجی، از نوع Exception درنظر گرفته شده‌است. مثالی دیگر در این زمینه:
var oneTwoThreeArray = () => new[]{1, 2, 3}; // inferred type is Func<int[]>
var oneTwoThreeList = IList<int> () => new[]{1, 2, 3}; // same body, but inferred type is now Func<IList<int>>

این natural return types، به method groups نیز بسط یافته‌است. منظور از method groups، متدهایی بدون ذکر لیست آرگومان‌های آن‌ها است:
Func<int> read = Console.Read;
Action<string> write = Console.Write;
این‌ها نیز در C# 10.0 به صورت خلاصه‌ی زیر قابل بیان هستند:
var read = Console.Read; // Just one overload; Func<int> inferred
var write = Console.Write; // ERROR: Multiple overloads, can't choose
البته در اینجا اگر متدی چندین overload داشته باشد، دیگر نمی‌توان از روش خلاصه شده‌ی فوق استفاده کرد.

و در آخر امکان تعریف ویژگی‌ها (attributes) نیز بر روی lambda expressions در C# 10.0 میسر شده‌است:
var choose = [Example(2)][Example(3)] object (bool b) => b ? 1 : "two";

پ.ن.
تمام این‌ها در جهت پشتیبانی و ساده کردن کار با Minimal APIs ارائه شده‌ی در ASP.NET Core 6x به زبان #C اضافه شده‌اند.
‫۲ سال و ۶ ماه قبل، شنبه ۷ اسفند ۱۴۰۰، ساعت ۱۲:۵۶
یک نکته‌ی تکمیلی: امکان تزریق وابستگی‌ها در سازنده‌ی کلاس‌های کامپوننت‌ها در Blazor 7x

اگر از روش code-behind جهت توسعه‌ی کامپوننت‌های Blazor استفاده می‌کنید، در دات نت 7 و Blazor 7x می‌توانید علاوه بر بکارگیری ویژگی [Inject]، از تزریق مستقیم در سازنده‌ی کلاس‌ها نیز استفاده کنید:
public class MyComponent : ComponentBase
{
   public MyComponent(IMyService myService) { ... }
}
اطلاعات بیشتر
‫۲ سال و ۶ ماه قبل، سه‌شنبه ۳ اسفند ۱۴۰۰، ساعت ۲۱:۰۱
یک نکته‌ی تکمیلی: از LowercaseQueryStrings استفاده نکنید!

به همراه نکات این مطلب، خاصیت دیگری به نام LowercaseQueryStrings نیز وجود دارد و به همان نحوی که LowercaseUrls تنظیم می‌شود، قابل تنظیم است، اما نحوه‌ی پردازش آن به صورت زیر است:
 if (_options.LowercaseUrls && _options.LowercaseQueryStrings) 
 { 
     queryString = queryString.ToLowerInvariant(); 
 }
یعنی کل کوئری استرینگ و حتی محتوای آن‌را هم تغییر می‌دهد. در این حالت برای مثال اگر اطلاعات کوئری استرینگ شما به همراه داده‌ای رمزنگاری شده باشد، به علت از دست رفتن و تخریب اصل اطلاعات، دیگر قابلیت رمزگشایی را نخواهد داشت.
‫۲ سال و ۶ ماه قبل، یکشنبه ۱ اسفند ۱۴۰۰، ساعت ۲۲:۳۲
یک نکته: اضافه شدن امکان بررسی وضعیت SDKهای نصب شده در دات نت 6

اگر پس از نصب SDK دات نت 6، دستور زیر را صادر کنید:
dotnet sdk check
یک چنین خروجی را مشاهده خواهید کرد:

این خروجی به همراه اطلاعات زیر است:
الف) چه نگارش‌های جدیدتری ارائه شده و آیا SDKهای نصب شده‌ی شما به روز است؟
ب) کدامیک به پایان پشتیبانی نزدیک شده‌است.
‫۲ سال و ۶ ماه قبل، شنبه ۳۰ بهمن ۱۴۰۰، ساعت ۲۰:۲۷
یک نکته‌ی تکمیلی: دات نت 6 و معرفی یک تایمر Async جدید

در این مطلب برای انجام کارهای پس زمینه‌ای متناوب و async، مجبور به اختراع تایمرهای خاصی شدیم که در دات نت 6، روش بهتری برای انجام آن ارائه شده‌است. تا پیش از دات نت 6، تایمرهای زیر در فضاهای نام مختلفی تعریف شده‌اند:

- System.Threading.Timer
- System.Timers.Timer
- System.Windows.Forms.Timer
- System.Web.UI.Timer
- System.Windows.Threading.DispatcherTimer

طراحی تمام این تایمرها مبتنی بر callbackها است و رخ‌دادهایی که توسط تایمر، در زمان مشخصی صادر می‌شوند. این تایمرها مشکلات زیر را به همراه دارند:
1- متد callback فراخوانی شده async نیست (زمانی طراحی شده بودند که نوع Task، هنوز وجود خارجی نداشت).
2- اگر درون callback خطایی رخ‌دهد، خاموش سازی تایمر نیاز به عملیات اضافه‌تری دارد.
3- اگر عملیات درون یک callback هنوز به پایان نرسیده باشد، ممکن است این callback مجددا فراخوانی شود.

برای رفع تمام این مشکلات، تایمر جدیدی به نام PeriodicTimer به دات نت 6 اضافه شده‌است که این مزایا را به همراه دارد:
1- تمام async است.
2- تنها یک جریان کاری مشخص را دارد که با فراخوانی دستی متد WaitForNextTickAsync آن، به میزان بازه‌ی زمانی مشخص شده، صبر خواهد شد. وجود تنها یک جریان کاری، مشکلات 2 و 3 تایمرهای قبلی را رفع می‌کند.

یک مثال:
private async Task DoTaskAsync()
{
   using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));   
   while (await timer.WaitForNextTickAsync())
   {
      Console.WriteLine($"Firing at {DateTime.Now}");
   }   
}
این تایمر async، هر 5 ثانیه یکبار، کدهای بدنه‌ی حلقه را اجرا می‌کند.

اگر خواستیم این تایمر، پس از 20 ثانیه به طور کامل متوقف شود، روش کار به صورت زیر است که توسط یک CancellationTokenSource که به عنوان پارامتر متد WaitForNextTickAsync ارسال می‌شود، قابل پیاده سازی است:
private async Task DoTaskAsync()
{
   try
   {
      var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));

      using var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));   
      while (await timer.WaitForNextTickAsync(cts.Token))
      {
         Console.WriteLine($"Firing at {DateTime.Now}");
      }   
   }
   catch (OperationCanceledException)
   {
       Console.WriteLine("Operation cancelled");
   }
}
این خاتمه‌ی خودکار، با صدور یک OperationCancelled exception رخ خواهد داد و یا حتی می‌توان متد ()cts.Cancel را نیز در صورت نیاز به صورت دستی در داخل حلقه فراخوانی کرد تا عملیات خاتمه یابد.