‫۲ سال و ۷ ماه قبل، یکشنبه ۱۹ دی ۱۴۰۰، ساعت ۱۴:۴۹
چند نکته‌ی تکمیلی
- در این لحظه فقط پروایدر SQLite مایکروسافت از نوع‌های DateOnly و TimeOnly توسط EF-Core پشتیبانی می‌کند.
- هنوز پشتیبانی از این نوع‌های جدید به زیرساخت Microsoft.Data.SqlClient اضافه نشده. به همین جهت EF-Core هم تا نگارش 6 آن چنین پشتیبانی را از این نوع‌ها برای SQL Server ارائه نمی‌دهد و حتی Migration آن هم از این نوع‌ها برای SQL Server پشتیبانی نمی‌کند.
- البته سایر پروایدرهای ثالث EF-Core مانند MySQL و PostgreSQL این پشتیبانی را اضافه کرده‌اند.
‫۲ سال و ۸ ماه قبل، سه‌شنبه ۷ دی ۱۴۰۰، ساعت ۱۶:۱۰
یک نکته: نمی‌توان تگ‌های اسکریپت را داخل فایل‌های کامپوننت‌های Blazor قرار داد
اگر سعی کنیم به هر دلیلی یک تگ script را داخل یک فایل کامپوننت Blazor قرار دهیم، با خطای کامپایلر زیر متوقف خواهیم شد:
error RZ9992: Script tags should not be placed inside components because they cannot be updated dynamically.
To fix this, move the script tag to the 'index.html' file or another static location. For more information see https://go.microsoft.com/fwlink/?linkid=872131
علت اینجا است که رندر مجدد کامپوننت‌ها در Blazor، می‌توانند سبب اجرای چندین باره‌ی یک چنین اسکریپت‌هایی شوند. به همین جهت تصمیم گرفته‌اند که inline-scripts را ممنوع کنند. برای رفع این خطا یا می‌توان به روشی که عنوان کرده عمل کرد و محتوای اسکریپت را به یک فایل مجزا منتقل کرد (همانند نکات این مطلب) و یا اگر به هر دلیلی نمی‌خواهیم این‌کار را انجام دهیم، می‌توان به صورت زیر عمل کرد تا خطای کامپایلر فوق ندید گرفته شود ( یعنی می‌دانیم که قرار است چه اتفاقی رخ دهد):
<script suppress-error="BL9992" />
- فلسفه‌ی کار با Blazor server، امکان دسترسی مستقیم به لایه سرویس‌های برنامه، بدون نیاز به طراحی یک Web API خاص آن‌ها است (کار با آن ساده‌تر است). اگر قرار است با Web API کار کنید، شاید بهتر باشد از WASM استفاده کنید.
- سرویس‌های Blazor Server، طول عمر خاصی دارند و تا زمانیکه اتصال برقرار است، از دست نمی‌روند. بنابراین خیلی از اطلاعات را می‌توان به صورت متداولی در آن‌ها ذخیره کرد و نیازی به تمهید خاصی نیست؛ هرچند encrypted local storage هم دارند.
- امکان طراحی interceptor برای HTTP Client هم وجود دارد تا هربار نیازی به مقدار دهی هدر Authorization نباشد.
- بله. در این سری اگر از Identity استفاده شده، بیشتر هدف مدیریت کاربران بوده و یا برای Blazor Server، دسترسی به کوکی خودکار پس از لاگین. نکات تهیه‌ی authentication provider سفارشی مطرح شده‌ی در قسمت wasm این سری برای کار با JWT، همه جا یکسان است و وابستگی به Identity و یا حتی Identity server پیش‌فرض مطرح شده‌ی توسط مایکروسافت، ندارد.
‫۲ سال و ۸ ماه قبل، شنبه ۲۷ آذر ۱۴۰۰، ساعت ۰۰:۰۴
یک نکته: استفاده از base href و url‌های برنامه
اگر قرار است base href را مقدار دهی کنید، در کدهای برنامه هیچ مسیری را با / شروع نکنید. شروع با / به معنای پردازش از ریشه‌ی سایت خواهد بود و نه از زیر پوشه‌ی برنامه. برای مثال اگر قرار است برنامه در مسیر http://site/app ارائه شود، اگر url ای را با / شروع کردید، به http://site اشاره می‌کند و نه http://site/app. این مورد حتی برای urlهای api‌ها هم باید رعایت شود و آن‌ها هم نباید با مثلا api/ شروع شوند که به ریشه‌ی سایت اشاره می‌کند. این مورد را باید به عنوان یک best practice، در حین توسعه‌ی برنامه‌های blazor رعایت کرد.
‫۲ سال و ۸ ماه قبل، جمعه ۲۶ آذر ۱۴۰۰، ساعت ۲۳:۵۰
یک نکته: base href حساس به بزرگی و کوچکی حروف است!
بین تنظیم
<base href="/blazor/" />
و تنظیم زیر
<base href="/Blazor/" />
تفاوت وجود دارد. یعنی اگر اولی تنظیم شده باشد و کاربر در مرورگر http://localhost/Blazor را وارد کند، با پیام خطای زیر مواجه می‌شود:
System.ArgumentException: The URI is not contained by the base URI
فعلا برای رفع این مشکل می‌توان قطعه کد زیر را پیش از تگ js مربوط به blazor قرار داد تا base href را به صورت پویا تنظیم کند:
<script>
  var path = window.location.pathname.split('/');
  var baseTag = document.getElementsByTagName('base');
  baseTag[0].setAttribute('href', '/' + path[1] + '/');
</script>
اطلاعات بیشتر
‫۲ سال و ۸ ماه قبل، یکشنبه ۲۱ آذر ۱۴۰۰، ساعت ۱۵:۰۰
یک نکته: روش تعیین اعتبار دستی یک JWT
اگر خواستید رشته‌ی JWT دریافتی را در سمت سرور و بر اساس تنظیمات ابتدایی برنامه، بدون نیاز به تکرار آن‌ها و به صورت دستی اعتبارسنجی کنید، روش انجام کار به صورت زیر است:
public class TokenFactoryService
{
    private readonly JwtBearerOptions _jwtBearerOptions;

    public TokenFactoryService(IOptionsSnapshot<JwtBearerOptions> jwtBearerOptions)
    {
        if (jwtBearerOptions == null)
        {
            throw new ArgumentNullException(nameof(jwtBearerOptions));
        }

        _jwtBearerOptions = jwtBearerOptions.Value ?? throw new ArgumentNullException(nameof(jwtBearerOptions));
    }

    // From: https://github.com/dotnet/aspnetcore/blob/a450cb69b5e4549f5515cdb057a68771f56cefd7/src/Security/Authentication/JwtBearer/src/JwtBearerHandler.cs
    public bool ValidateJwt(string token)
    {
        foreach (var validator in _jwtBearerOptions.SecurityTokenValidators)
        {
            try
            {
                if (validator.CanReadToken(token))
                {
                    validator.ValidateToken(token, _jwtBearerOptions.TokenValidationParameters, out _);
                }
            }
            catch
            {
                return false;
            }
        }

        return true;
    }
}
‫۲ سال و ۸ ماه قبل، یکشنبه ۲۱ آذر ۱۴۰۰، ساعت ۱۴:۰۶
نکته‌ای در مورد Owned Entities از EF-Core 3.1 به بعد

از EF-Core 3.1 به بعد، زمان حذف اطلاعات وابسته‌ی به یک رکورد تغییر کرده‌است. برای مثال اگر رکوردی به همراه یک owned entity باشد و سعی کنیم آ‌ن‌را حذف کنیم و این حذف هم از نوع soft delete باشد، کوئری حاصل به همراه نال کردن مقادیر این owned entity هم خواهد بود (حتی اگر این اطلاعات نال‌پذیر هم نباشند). برای بازگشت به سیستم قبلی و حذف اطلاعات وابسته پس از حذف رکورد اصلی و نه قبل از آن، باید به صورت زیر عمل کرد:
// To fix https://github.com/dotnet/efcore/issues/19786
context.ChangeTracker.CascadeDeleteTiming = CascadeTiming.OnSaveChanges;
context.ChangeTracker.DeleteOrphansTiming = CascadeTiming.OnSaveChanges;