‫۳ سال و ۹ ماه قبل، پنجشنبه ۲۹ آبان ۱۳۹۹، ساعت ۲۳:۳۰
یک نکته‌ی تکمیلی
روش تبدیل/سریالایز سریع یک شیء، به آرایه‌ای از بایت‌های UTF8:
var resultBytes = JsonSerializer.SerializeToUtf8Bytes(data,
                    new JsonSerializerOptions { WriteIndented = false, IgnoreNullValues = true });
و برعکس، تبدیل آرایه‌ای از بایت‌های UTF8 که به روش فوق سریالایز شده به شیء اصلی:
var data = JsonSerializer.Deserialize<T>(new ReadOnlySpan<byte>(resultBytes));
‫۳ سال و ۹ ماه قبل، دوشنبه ۲۶ آبان ۱۳۹۹، ساعت ۱۳:۵۹
به روز رسانی در مورد ابزار dotnet-outdated 
اگر پیشتر از ابزار dotnet-outdated برای به روز رسانی سراسری بسته‌های نیوگت پروژه‌های خود استفاده می‌کردید، این ابزار دیگر نگهداری نمی‌شود و به dotnet-outdated-tool تغییر نام یافته‌است. در این حالت اجرای دستورات زیر، در یک فایل bat و یا cmd در کنار فایل sln شما، کار به روز رسانی یک‌دست تمام وابستگی‌ها را انجام می‌دهد؛ البته اگر نیاز به به روز رسانی به net5 را داشته باشید، باید ابتدا یک سطر <TargetFramework>net5.0</TargetFramework> را نیز در تمام فایل‌های csproj خود اصلاح کنید و بعد اجرای دستوارت زیر:
dotnet restore
setx DOTNET_HOST_PATH "%ProgramFiles%\dotnet\dotnet.exe" /M
dotnet tool uninstall --global dotnet-outdated
dotnet tool update --global dotnet-outdated-tool
dotnet outdated -u
dotnet restore
pause
‫۳ سال و ۹ ماه قبل، یکشنبه ۲۵ آبان ۱۳۹۹، ساعت ۱۷:۳۱
تغییرات پردازش تاریخ از زمان ارائه‌ی ASP.NET Core 3.1

از زمان ارائه‌ی کتابخانه‌ی JSON توکار در ASP.NET Core 3x، نحوه‌ی پردازش تاریخ‌های دریافتی در برنامه‌های ASP.NET Core نیز تغییر کرد و در حالت دریافت اطلاعات به صورت JSON در اکشن متدها، فقط بر اساس ISO 8601-1:2019 عمل می‌کند. یعنی اگر تاریخ را با فرمت متداول 2019/06/14 به سمت سرور ارسال کنید، خطای ModelState زیر را دریافت خواهید کرد:
{
    "$.time": [
        "The JSON value could not be converted to System.DateTime. Path: $.time | LineNumber: 1 | BytePositionInLine: 23."
    ]
}
که عنوان می‌کند تاریخ ارسالی قابل پردازش نیست. چون فرمت دریافتی آن باید به این صورت باشد:
2019-06-14T02:30:04.0576719Z
و در کل این فرمت‌ها را قبول می‌کند:
'Full date'

    "yyyy'-'MM'-'dd"

"'Full date''T''Hour'':''Minute'"

    "yyyy'-'MM'-'dd'T'HH':'mm"

"'Full date''T''Partial time'"

    "yyyy'-'MM'-'dd'T'HH':'mm':'ss" (The Sortable ("s") Format Specifier)
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF"

"'Full date''T''Time hour'':''Minute''Time offset'"

    "yyyy'-'MM'-'dd'T'HH':'mmZ"
    "yyyy'-'MM'-'dd'T'HH':'mm('+'/'-')HH':'mm"

'Date time'

    "yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFFZ"
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss('+'/'-')HH':'mm"
    "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'FFFFFFF('+'/'-')HH':'mm"


تغییرات پردازش تاریخ در ASP.NET Core 5x

در نگارش‌های قبلی ASP.NET Core، اگر تاریخی از طریق کوئری استرینگ و با فرمت ISO فوق دریافت می‌شد، مانند:
https://localhost:44393/time?time=2019-06-14T02:30:04.0576719Z
زمانیکه در سمت سرور پردازش می‌شد، نوع آن به اشتباه به Local ترجمه می‌شد و UTC آن درنظر گرفته نمی‌شد:
Model-bound value is: 2019-06-13T19:30:04.0576719-07:00, Kind is: Local
این مشکل در ASP.NET Core 5x برطرف شده‌است و اینبار Kind تاریخ پردازش شده، از نوع UTC ارسالی است.

یک نکته: ویژگی‌های BindProperty و DisplayFormat در razor pages، امکان تغییر فرمت ورودی و نمایشی را می‌دهند:
[BindProperty, DisplayFormat(DataFormatString = "{0:yyyy-MM-ddTHH:mm}", ApplyFormatInEditMode = true)]
public DateTime DateTime { get; set; }

DateTime: <input asp-for="DateTime"  asp-format="{0:yyyy-MM-ddTHH:mm}" />
‫۳ سال و ۹ ماه قبل، پنجشنبه ۲۲ آبان ۱۳۹۹، ساعت ۱۱:۴۹
یک نکته‌ی تکمیلی: تداخل ReSharper قدیمی، با C# 9.0
اگر از ویژوال استودیو استفاده می‌کنید و پس از ارتقاء آن به آخرین نگارش، ویژگی‌های جدید C# 9.0 در ادیتور آن تشخیص داده نمی‌شوند .... مشکل از داشتن ReSharper قدیمی است:

‫۳ سال و ۱۰ ماه قبل، سه‌شنبه ۲۰ آبان ۱۳۹۹، ساعت ۱۴:۱۷
یک نکته‌ی تکمیلی: خواص init-only در زمان اجرای برنامه read-only نیستند.
تمام مواردی که در مطلب جاری بحث شدند، مرتبط با زمان کامپایل هستند. در زمان اجرای برنامه و با استفاده از reflection، می‌توان مقادیر init-only را همانند سایر خواص ;get; set دار، تنظیم کرد:
PropertyInfo propertyInfo = typeof(Person).GetProperty(nameof(User.Name));
propertyInfo.SetValue(user, "edited");
Console.WriteLine(user.Name); // Print "edited"

همچنین اینگونه خواص را توسط reflection بر اساس ویژگی IsExternalInit آن‌ها می‌توان تشخیص داد:

public static bool IsInitOnly(this PropertyInfo propertyInfo)
{
    MethodInfo setMethod = propertyInfo.SetMethod;
    if (setMethod == null)
        return false;
var isExternalInitType = typeof(System.Runtime.CompilerServices.IsExternalInit);
    return setMethod.ReturnParameter.GetRequiredCustomModifiers().Contains(isExternalInitType);
}
مانند:
PropertyInfo propertyInfo = typeof(Person).GetProperty(nameof(Person.Name));
var isInitOnly = propertyInfo.IsInitOnly();
‫۳ سال و ۱۰ ماه قبل، سه‌شنبه ۲۰ آبان ۱۳۹۹، ساعت ۱۳:۵۶
معادل‌های string.IsNullOrEmpty در C# 9.0 جهت اطلاع و آشنایی با Syntax جدید؛ اگر جائی آن‌ها را دیدید!
using System;

namespace CS9Features
{
    public static class ModernizingACodebase
    {
        public static void PropertyPatternToReplaceIsNullorEmpty1()
        {
            string hello = null;

            // Old approach
            if (!string.IsNullOrEmpty(hello))
            {
                Console.WriteLine($"{hello} has {hello.Length} letters.");
            }

            // New approach, with a property pattern
            if (hello is { Length: > 0 })
            {
                Console.WriteLine($"{hello} has {hello.Length} letters.");
            }
        }

        public static void PropertyPatternToReplaceIsNullorEmpty2()
        {
            // For arrays
            var greetings = new string[2];
            greetings[0] = "Hello world";
            greetings = null;

            // Old approach
            if (greetings != null && !string.IsNullOrEmpty(greetings[0]))
            {
                Console.WriteLine($"{greetings[0]} has {greetings[0].Length} letters.");
            }

            // New approach
            if (greetings?[0] is { Length: > 0 } hi)
            {
                Console.WriteLine($"{hi} has {hi.Length} letters.");
            }
        }
    }
}
برای مطالعه‌ی بیشتر
‫۳ سال و ۱۰ ماه قبل، دوشنبه ۱۲ آبان ۱۳۹۹، ساعت ۰۰:۵۶
- کتابخانه‌ای که ذکر کردید، از روش symbol server نیوگت استفاده می‌کند (که در بحث جاری مطرح شده) و نه قرار دادن فایل‌های pdb در بسته‌ی نیوگت. به همین جهت ارتباطی به issue ای که ارسال کردید و در مورد pdbهای embedded هست، ندارد و فایل‌های pdb دریافتی از symbol server، در پوشه‌ی bin کپی نمی‌شوند و در صورت دریافت، سراسری هستند (ذخیره در کش عمومی سیستم و بارگذاری مجدد از همان کش).
- هدف از source link این هست که بتوان قطعه کد کتابخانه‌ی ثالثی را در حین دیباگ مشاهده کرد. هدف از pdb دریافتی از nuget هم این است که اگر در حین کار با کتابخانه‌ای به استثنائی رسیدید، اطلاعات دیباگ بیشتری مانند شماره سطر کدهای مرتبط با آن کتابخانه را نمایش دهد و هر دو مورد هم بدون هیچ تنظیم اضافه‌تری در فایل csproj، با VSCode کار می‌کنند.

یک مثال با VSCode:
فایل launch.json پروژه به این صورت تغییر کرد (بر اساس توضیحات انتهای مطلب):
{
    // Use IntelliSense to find out which attributes exist for C# debugging
    // Use hover for the description of the existing attributes
    // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
    "version": "0.2.0",
    "configurations": [
        {
            "name": ".NET Core Launch (console)",
            "type": "coreclr",
            "request": "launch",
            "preLaunchTask": "build",
            // If you have changed target frameworks, make sure to update the program path.
            "program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/EFCoreDbFunctionsSample.dll",
            "args": [],
            "cwd": "${workspaceFolder}",
            // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console
            "console": "internalConsole",
            "stopAtEntry": false,
            "justMyCode": false,
            "symbolOptions": {
                "searchMicrosoftSymbolServer": true
            },
            "suppressJITOptimizations": true,
            "env": {
                "COMPlus_ZapDisable": "1"
            }
        },
        {
            "name": ".NET Core Attach",
            "type": "coreclr",
            "request": "attach",
            "processId": "${command:pickProcess}"
        }
    ]
}
در این زمان با فشردن دکمه‌ی F5 در VSCode، کار دریافت symbols از symbols server شروع می‌شود (و کمی طول می‌کشد و در لاگ پروژه، مراحل آن کاملا مشخص هست). در این حالت فایل‌های pdb را هم داخل پوشه‌ی bin\Debug\netcoreapp3.1 کپی نمی‌کند و در کش سراسری nuget در سیستم قرار می‌دهد تا به ازای هر پروژه، این اطلاعات تکراری حجیم (به ازای هر dll مرتبط با پروژه، یک فایل pdb حجیم از symbol server دریافت خواهد شد)، دریافت نشوند:
Loaded 'C:\Program Files\dotnet\shared\Microsoft.NETCore.App\3.1.8\System.Private.CoreLib.dll'. Symbols loaded.
Loaded 'D:\Prog\1399\EFCoreDbFunctionsSample\bin\Debug\netcoreapp3.1\EFCoreDbFunctionsSample.dll'. Symbols loaded.
.
.
.
Loaded 'D:\Prog\1399\EFCoreDbFunctionsSample\bin\Debug\netcoreapp3.1\EFCoreSecondLevelCacheInterceptor.dll'. Symbols loaded.
.
.
.
همانطور که مشاهده می‌کنید، Symbols مربوط به کتابخانه‌ی ثالث استفاده شده هم بارگذاری شده‌اند.

در مورد سورس لینک:
قرار دادن یک break-point روی یک سطر:


و سپس زمانیکه در حالت دیباگ (همان فشردن دکمه‌ی F5 در VSCode)، به این سطر رسیدیم، فشردن دکمه‌ی F11، تا سورس متناظر بارگذاری شود: