‫۴ روز قبل، جمعه ۲۳ شهریور ۱۴۰۳، ساعت ۰۸:۰۲

یک نکته‌ی تکمیلی: روش اجرای پیش‌فرض کارهای پس زمینه، ترتیبی است.

به صورت پیش‌فرض، اجرا و خاتمه‌ی تمام سرویس‌های انجام کارهای پس‌زمینه، ترتیبی و هر کدام از آن‌ها، یکی پس از دیگری شروع به کار می‌کنند. اگر علاقمند باشید تا این کارها به صورت موازی اجرا شوند، از دات‌نت 8 به بعد می‌توان تنظیم زیر را جهت مشخص کردن نحوه‌ی مدیریت اجرای کارهای پیش‌زمینه، به برنامه اضافه کرد:

builder.Services.Configure<HostOptions>(options =>
{
    options.ServicesStartConcurrently = true;
    options.ServicesStopConcurrently = true;
});

مزیت اینکار، شروع و همچنین پایان سریعتر برنامه، با داشتن تعداد زیادی کار پس‌زمینه است.

یک نکته‌ی تکمیلی: SemaphoreSlim، کمی سنگین است و امکانات سفارشی سازی کمی هم دارد؛ اگر به‌دنبال یک نمونه‌ی سریعتر و با مصرف حافظه‌ی کمتری هستید، کتابخانه‌ی AsyncKeyedLock مفید‌تر است. این کتابخانه را می‌توان جایگزین neosmart/AsyncLock درنظر گرفت.

‫۸ روز قبل، یکشنبه ۱۸ شهریور ۱۴۰۳، ساعت ۱۵:۳۰

یک نکته‌ی تکمیلی: روش نمایش خودکار آرگومان‌های نامدار در Rider

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

الف) ویژگی فرمت کردن کدها را در حالت ذخیره سازی تغییرات، فعال کنید:

با اینکار، هربار که تغییرات را ذخیره می‌کنید، تنظیمات کدنویسی، به صورت خودکار به فایل‌های ذخیره نشده‌، اعمال می‌شوند.

ب) به قسمت Settings -> Editor -> Cody Style -> C# -> Syntax Style مراجعه کرده و در قسمت تنظیمات آرگومان‌ها، حداقل گزینه‌های Literal values و String literal values را بر روی named argumets قرار دهید تا نکات مطلب جاری، به صورت خودکار اعمال شوند:

همانطور که در مثال before/after تصویر فوق هم مشخص است، مزیت اینکار، مفهوم پیدا کردن اعداد و رشته‌های وارد شده به عنوان آرگومان‌های متدها هستند.

‫۱۰ روز قبل، جمعه ۱۶ شهریور ۱۴۰۳، ساعت ۱۲:۱۷

یک نکته‌ی تکمیلی: امکان کار همزمان هم با HttpClient وجود دارد!

تا پیش از ارائه‌ی NET Core.، روش متداول دریافت فایل‌ها، عموما به صورت زیر و همزمان/synchronous بود:

var client = new WebClient();
client.DownloadFile(downloadUrl, filePath);

هرچند ... WebClient امکان دریافت فایل‌ها را به صورت غیرهمزمان هم دارد، اما API آن با async/await هماهنگ نیست و طراحی آن قدیمی است.

پس از آن،‌ HttpClient ارائه شد که از روز اول، async بود و کاملا هماهنگ با async/await و روش کدنویسی جدید آن. اما ... شاید در قسمت‌هایی نیاز باشد تا بتوان کدهای قدیمی را بدون تبدیل کردن آن‌ها به نمونه‌های async، به همان شکل همزمان، بازنویسی کنیم. برای رفع این مشکل، از زمان دات‌نت 5، متد Send همزمان هم به API آن اضافه شده‌است:

var response = httpClient.Send(new HttpRequestMessage(HttpMethod.Post, "http://site.com"));
using var reader = new StreamReader(response.Content.ReadAsStream());            
var content = reader.ReadToEnd();
‫۱۱ روز قبل، پنجشنبه ۱۵ شهریور ۱۴۰۳، ساعت ۲۲:۱۰

یک نکته‌ی تکمیلی: روش لغو صف درخواست‌های مکرر fetch ارسالی به سمت سرور

ورودی جستجوی بالای صفحه‌ را درنظر بگیرید که به‌ازای هربار ورود حرفی، یک درخواست fetch جدید را به سمت سرور ارسال می‌کند تا نتایج جستجوی حاصل را دریافت کند. مشکل اینجاست که ما تنها به آخرین درخواست fetch ارسال شده‌ی به سمت سرور نیاز داریم و نه به تمام درخواست‌های دیگری که صادر شده‌اند. به همین جهت این صف درخواست‌های fetch قبلی، غیربهینه بوده و ترافیک بالایی را سبب می‌شوند. یک روش مواجه شدن با این مساله، استفاده از مفهومی به نام debounce است که در پشت صحنه، از یک تایمر استفاده می‌کند و فقط هر چند ثانیه یکبار، یک درخواست جدید را به همراه آخرین متن ورودی، به سمت سرور ارسال خواهد کرد. راه دیگری هم برای مواجه شدن با این مشکل، در مرورگرهای جدید پیش‌بینی شده‌است که AbortController نام دارد. با استفاده از آن می‌توان «سیگنالی» را به صف درخواست‌های پرتعداد fetch قبلی حاصل از ورود اطلاعات کاربر ارسال کنیم که ... «لغو شوید» و به سمت سرور ارسال نشوید.

برای توضیح بهتر آن، به مثال زیر دقت کنید:

<!DOCTYPE html>
<html>
  <body>
    <input id="search" type="number" />
    <script>
      const results = [];
      const search = document.getElementById("search");

      let controller = new AbortController();
      let signal = controller.signal;

      const onChange = () => {
        const value = search.value;
        if (value) {
          controller.abort();
          controller = new AbortController();
          signal = controller.signal;
          getPost(value, signal);
        }
      };
      search.onkeyup = onChange;
    </script>
  </body>
</html>

- در اینجا یک input box را داریم که ابتدا، یافت شده و سپس به رخ‌داد onkeyup آن، متد onChange نسبت داده شده‌است تا هربار که کاربر، اطلاعاتی را وارد می‌کند، فراخوانی شود.

- در ابتدای اسکریپت هم نحوه‌ی نمونه سازی شیء استاندارد جاوااسکریپتی AbortController و دسترسی به شیء signal آن‌را مشاهده می‌کنید.

- در متد onChange، ابتدا مقدار جدید ورودی کاربر، دریافت می‌شود، سپس این AbortController، لغو می‌شود و بعد یک نمونه‌ی جدید از آن ایجاد شده و مجددا به شیء signal آن دسترسی پیدا می‌کنیم تا آن‌را به متد getPost ارسال کنیم. این متد هم چنین پیاده سازی را دارد:

const getPost = (value, signal) => {
          fetch(
            `https://site.com/search/${value}`,
            { signal }
          );          
      };

همانطور که مشاهده می‌کنید، تابع fetch، قابلیت پذیرش شیء signal را هم دارد. زمانیکه با هربار تایپ کاربر، متد ()controller.abort فراخوانی می‌شود، سیگنالی را به fetch «قبلی» متصل به آن ارسال می‌کند که ... دیگر به سمت سرور ارسال نشو و متوقف شو. با اینکار فقط آخرین ورودی کاربر، سبب بروز یک fetch موفق می‌شود و ترافیک ارسالی به سمت سرور کاهش پیدا می‌کند (چون تمام fetchهای قبلی، سیگنال abort را دریافت کرده‌اند)؛ مانند مثال زیر که کاربر، 5 بار حروفی را وارد کرده و به ازای هربار ورود حرفی، یک درخواست fetch جدید، ایجاد شده، اما ... فقط آخرین درخواست ارسالی او موفق بوده و نتیجه‌ای را بازگشت داده و مابقی درخواست‌ها ... abort شده‌اند. این عملیات abort، در سمت کاربر اعمال می‌شود؛ یعنی اصلا درخواستی به سمت سرور ارسال نمی‌شود و این لغو درخواست، توسط برنامه‌ی سمت سرور انجام نشده‌است.

‫۱۳ روز قبل، چهارشنبه ۱۴ شهریور ۱۴۰۳، ساعت ۰۸:۱۴

یک نکته‌ی تکمیلی: بین مسیرهای مطلق و نسبی در لینوکس و ویندوز، تفاوت وجود دارد!

فرض کنید با استفاده از قطعه کد زیر، سعی در تبدیل یک مسیر نسبی را به مسیری مطلق، داریم:

private string GetAbsoluteApiUrl(string url)
    => Uri.TryCreate(url, UriKind.Absolute, out _) ? url : NavigationManager.ToAbsoluteUri(url).ToString();

این قطعه کد در ویندوز بدون مشکل کار می‌کند. برای مثال اگر مسیر api/test/ را به آن معرفی کنیم، آن‌را تبدیل به یک مسیر مطلق شروع شده‌ی با آدرس دامنه‌ی سایت می‌کند. اما ... این قطعه کد در لینوکس کار نمی‌کند! چون مسیر api/test/ در لینوکس، یک مسیر مطلق به‌شمار می‌رود! برای رفع این مشکل، قرار است چنین API ای در آینده اضافه شود:

new Uri("/foo", new UriCreationOptions { AllowImplicitFilePaths = false });

‫۱۵ روز قبل، دوشنبه ۱۲ شهریور ۱۴۰۳، ساعت ۰۷:۴۱

یک نکته‌ی تکمیلی: غنی سازی کامپایلر سی‌شارپ جهت نمایش اخطارهایی در مورد متدهایی بیش از اندازه پیچیده

پس از فعالسازی یکسری از آنالایزرها، اکنون می‌توان بررسی cyclomatic complexity را هم به آن‌ها سپرد. برای اینکار باید مراحل زیر طی شوند:

ابتدا یک سطر زیر را به فایل editorconfig. اضافه کنید:

dotnet_diagnostic.CA1502.severity = warning

سپس فایل جدید CodeMetricsConfig.txt را به ریشه‌ی پروژه اضافه کرده و سطر زیر را به آن اضافه کنید:

CA1502: 20

مقدار پیش‌فرض آستانه‌ی گزارش خطا در اینجا، 25 است که به روش فوق، قابل بازنویسی است.

البته نیاز است این فایل را به صورت یک فایل اضافی، به فایل csproj. نیز معرفی کرد:

<ItemGroup>
   <AdditionalFiles Include="CodeMetricsConfig.txt"/>
</ItemGroup>

همچنین می‌توان تنظیمات آستانه‌ی ریزتری را هم به متدها، نوع‌ها و غیره اعمال کرد:

CA1505(Method): 5
CA1505(Type): 15

مقادیر مجاز در اینجا، شامل SymbolKind, Assembly, Namespace, Type, Method, Field, Event,Property هستند.

در این فایل می‌توان آستانه‌ی گزارش خطای موارد زیر را هم بازنویسی کرد:

CA1501: Avoid excessive inheritance
CA1502: Avoid excessive complexity (this one)
CA1505: Avoid unmaintainable code
CA1506: Avoid excessive class coupling