۳ سال قبل، شنبه ۶ شهریور ۱۴۰۰، ساعت ۱۷:۴۹
۳ سال قبل، پنجشنبه ۴ شهریور ۱۴۰۰، ساعت ۲۳:۲۹
۳ سال قبل، پنجشنبه ۴ شهریور ۱۴۰۰، ساعت ۱۱:۵۴
۳ سال قبل، چهارشنبه ۲۷ مرداد ۱۴۰۰، ساعت ۱۳:۰۹
۳ سال قبل، چهارشنبه ۲۷ مرداد ۱۴۰۰، ساعت ۰۰:۴۱
- از برنامههایی مانند «AnotherRedisDesktopManager » استفاده کنید.
- «یکبار» که سرور Redis اجرا شد (نه چند بار)، روش کار با آن مانند SQL Server است. فقط در اینجا در رشتهی اتصالی که ساخته میشود، نام یا شماره دیتابیس را هم باید مشخص کنید؛ مانند WithDatabase(0). یا حتی میتوانید با یک دیتابیس هم کار کنید، اما برای cache-keyها پیشوند تعیین کنید تا با هم تداخل نکنند: UseCacheKeyPrefix (این روش توسط نویسندهی اصلی Redis هم توصیه شدهاست)
- بله. همانیکه که آرشیو نشده.
+ redis را باید با داکر اجرا کنید؛ اگر آخرین نگارش آنرا میخواهید.
+ CacheManager.Core را با easy-caching جایگزین کنید چون دیگر توسط نویسندهی آن نگهداری نمیشود.
۳ سال قبل، دوشنبه ۲۵ مرداد ۱۴۰۰، ساعت ۱۳:۲۷
یک نکتهی تکمیلی: روش تعریف data binding دو طرفه در کامپوننتها
در مطلب جاری، binding دو طرفه بررسی شد؛ که نکتهی مورد بحث آن، به ویژگیهای استاندارد HTML مانند ویژگی value یک input استاندارد اختصاص داشت. اما اگر بخواهیم در کامپوننتهای سفارشی خود، این binding دو طرفه را تعریف کنیم تا قابل اعمال به خواص و ویژگیهای #C باشد (مانند bind-ProprtyName@)، روش کار به نحو دیگری است. نمونهی آن کامپوننت استاندارد InputText خود Blazor است که در اینجا هم دارای bind-Value@ است؛ اما این Value (شروع شدهی با حروف بزرگ) یک خاصیت #C تعریف شدهی در کلاس InputText است و نه یک ویژگی استاندارد HTML که عموما با حروف کوچک شروع میشوند:
<InputText @bind-Value="employee.FirstName" />
الف) یک پارامتر عمومی به نام Value باید در کلاس کامپوننت جاری تعریف شود تا بتوان از طریق والد آن، مقداری را دریافت کرد (یک طرف binding به این نحو تشکیل میشود):
[Parameter] public string Value { set; get; }
[Parameter] public EventCallback<string> ValueChanged { get; set; }
نکتهی مهم: در اینجا بجای EventCallback، از Action هم میتوان استفاده کرد:
تفاوت اصلی و مهم آن با EventCallback، در فراخوانی نشدن خودکار متد StateHasChanged، در پایان کار آن است. زمانیکه EventCallback ای فراخوانی میشود، Blazor به صورت خودکار در پایان کار آن، متد StateHasChanged را نیز فراخوانی میکند تا والد دربرگیرندهی کامپوننت جاری، مجددا رندر شود (به همراه تمام کامپوننتهای فرزند آن). اما <Action<T فاقد این درخواست خودکار رندر و به روز رسانی مجدد UI است.
[Parameter] public Action<string> ValueChanged { get; set; }
ج) برای فعالسازی اعتبارسنجی استاندارد فرمهای Blazor، نیاز به خاصیت ویژهی سومی نیز هست (که اختیاری است):
[Parameter] public Expression<Func<string>> ValueExpression { get; set; }
مرحلهی آخر این طراحی، فراخوانی پارامتر ValueChanged است تا به کامپوننت والد این تغییرات را اطلاع رسانی کنیم. روش استاندارد آن به صورت زیر است:
private string _value; [Parameter] public string Value { get => _value; set { var hasChanged = string.Equals(_value, value, StringComparison.Ordinal); if (hasChanged) { _value = value; if (ValueChanged.HasDelegate) { _ = ValueChanged.InvokeAsync(value); } } } }
در این قطعه کد، بررسی ValueChanged.HasDelegate را هم مشاهده میکنید. زمانیکه پارامتر Value ای با طی سه مرحلهی فوق تعریف شد، قرار نیست حتما توسط bind-Value@ مورد استفاده قرار گیرد. میتوان Value را به صورت یک طرفه هم مورد استفاده قرار داد. در این حالت دو پارامتر ب و ج دیگر توسط Blazor ایجاد و مقدار دهی نشده و رهگیری نخواهند شد. یعنی تعریف bind-Value@ در سمت والد، معادل سیم کشی خودکار به ValueChanged و ValueExpression از طرف Blazor است و تعریف دستی آنها ضرورتی ندارد. اما میتوان bind-Value@ را هم تعریف نکرد و فقط نوشت Value. در این حالت از تنظیمات ب و ج صرفنظر میشود. بنابراین ضروری است که بررسی کنیم آیا پارامتر ValueChanged واقعا متصل به روال رویدادگردانی شدهاست یا خیر. اگر خیر، نیازی به اطلاع رسانی و فراخوانی متد ValueChanged.InvokeAsync نیست.
۳ سال قبل، یکشنبه ۲۴ مرداد ۱۴۰۰، ساعت ۱۳:۳۸
روش استفاده از TypeScript در پروژههای Blazor
شاید علاقمند باشید تا اسکریپتهای مورد نیاز یک پروژهی Blazor را با TypeScript تهیه کنید؛ تا از مزایای بررسی نوعها، intellisense قوی، null checking و غیره بهرهمند شوید و سپس توسط کامپایلر آن، حاصل را به کدهای نهایی js تبدیل کنید. برای اینکار میتوان مراحل زیر را طی کرد:
الف) تهیه فایل تنظیمات کامپایلر TypeScript
نیاز است فایل tsconfig.json را در ریشهی پروژه، جائیکه فایل csproj قرار دارد، با محتوای زیر ایجاد کرد:
{ "compilerOptions": { "strict": true, "removeComments": false, "sourceMap": false, "noEmitOnError": true, "target": "ES2020", "module": "ES2020", "outDir": "wwwroot/scripts" }, "include": [ "Scripts/**/*.ts" ], "exclude": [ "node_modules" ] }
ب) فعالسازی کامپایلر TypeScript به ازای هر بار build برنامه
برای اینکار نیاز است فایل csproj را به صورت زیر تکمیل کرد:
<Project Sdk="Microsoft.NET.Sdk.Razor"> <ItemGroup> <PackageReference Include="Microsoft.TypeScript.MSBuild" Version="4.3.5"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> <ItemGroup> <Content Remove="tsconfig.json" /> </ItemGroup> <ItemGroup> <TypeScriptCompile Include="tsconfig.json"> <CopyToOutputDirectory>Never</CopyToOutputDirectory> </TypeScriptCompile> </ItemGroup> </Project>
ج) یک مثال از تبدیل کدهای js به ts
فرض کنید کدهای سراسری زیر را داریم که به شیء window اضافه شدهاند:
window.exampleJsFunctions = { showPrompt: function (message) { return prompt(message, 'Type anything here'); } };
namespace JSInteropWithTypeScript { export class ExampleJsFunctions { public showPrompt(message: string): string { return prompt(message, 'Type anything here'); } } } export function showPrompt(message: string): string { var fns = new JSInteropWithTypeScript. ExampleJsFunctions(); return fns.showPrompt(message); }
د) روش استفاده از خروجی کامپایل شدهی TypeScript در کامپوننتهای Blazor
پس از کامپایل قطعه کد فوق، ابتدا مسیر قابل دسترسی به فایل js قرار گرفته شده در پوشهی wwwroot را مشخص میکنیم که همواره با الگوی زیر است. همچنین اینبار IJSObjectReference است که امکان دسترسی به export function یاد شده را میسر میکند:
private const string ScriptPath = "./_content/----namespace-here---/scripts/file.js"; private IJSObjectReference scriptModule;
protected override async Task OnAfterRenderAsync(bool firstRender) { if (scriptModule == null) scriptModule = await JSRuntime.InvokeAsync<IJSObjectReference>("import", ScriptPath);
await scriptModule.InvokeVoidAsync("exported fn name", params);
- ابتدا باید این کامپوننت، IAsyncDisposable را پیاده سازی کند:
public partial class MyComponent : IAsyncDisposable
public async ValueTask DisposeAsync() { if (scriptModule != null) { await scriptModule.DisposeAsync(); } }
۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۳:۵۵
چون متد VerifyPasswordAsync به صورت protected تعریف شده و public نیست. روش عمومی کردن آن با همان نام بکار رفتهی در کلاس پایه، به صورتی است که مشاهده میکنید.
۳ سال قبل، شنبه ۲۳ مرداد ۱۴۰۰، ساعت ۰۱:۴۲
Identity به همراه اینترفیس برای سرویسها و کلاسهای اصلی آن نیست و «... از این جهت که در سایر لایههای برنامه نمیخواهیم از تزریق مستقیم کلاسها استفاده کنیم؛ بلکه میخواهیم اینترفیس ها
را در موارد ضروری، به سازندههای کلاسهای تزریق
نمائیم ...» به همین جهت متدهای مورد نیاز را استخراج و استفاده میکنیم.
۳ سال قبل، پنجشنبه ۲۱ مرداد ۱۴۰۰، ساعت ۱۲:۱۳
یک نکتهی تکمیلی
RandomNumberGenerator ای که در این مطلب بحث شد، از NET Core 3.1. به بعد سادهتر شده و به همراه متد GetInt32 نیز هست:
public int GetRandomNumber() => RandomNumberGenerator.GetInt32(1, 1000000);