ObservableCollection در Entity Framework
Asp.Net Identity #2
در مرحلهی بعد سه پکیج زیر را باید نصب کنیم :
- Microsoft.AspNet.Identity.EntityFramework
- Microsoft.AspNet.Identity.OWIN
- Microsoft.Owin.Host.SystemWeb
<connectionStrings> <add name="IdentityDb" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=IdentityDb;Integrated Security=True;Connect Timeout=15;Encrypt=False;TrustServerCertificate=False;MultipleActiveResultSets=True"/> </connectionStrings>
<appSettings> <add key="webpages:Version" value="3.0.0.0" /> <add key="webpages:Enabled" value="false" /> <add key="ClientValidationEnabled" value="true" /> <add key="UnobtrusiveJavaScriptEnabled" value="true" /> <add key="owin:AppStartup" value="Users.IdentityConfig" /> </appSettings>
ساخت کلاس User
using System; using Microsoft.AspNet.Identity.EntityFramework; namespace Users.Models { public class AppUser : IdentityUser { // پروپرتیهای اضافی در اینجا } }
ساخت کلاس Database Context برنامه
using System.Data.Entity; using Microsoft.AspNet.Identity.EntityFramework; using Users.Models; namespace Users.Infrastructure { public class AppIdentityDbContext : IdentityDbContext<AppUser> { public AppIdentityDbContext() : base("IdentityDb") { } static AppIdentityDbContext() { Database.SetInitializer<AppIdentityDbContext>(new IdentityDbInit()); } public static AppIdentityDbContext Create() { return new AppIdentityDbContext(); } } public class IdentityDbInit : DropCreateDatabaseIfModelChanges<AppIdentityDbContext> { protected override void Seed(AppIdentityDbContext context) { PerformInitialSetup(context); base.Seed(context); } public void PerformInitialSetup(AppIdentityDbContext context) { // initial configuration will go here } } }
ساخت کلاس User Manager
کلاس UserManager حاوی متدهای بالا است. اگر دقت کنید، میبینید که تمامی متدهای بالا به کلمهی Async ختم میشوند. زیرا Asp.Net Identity تقریبا کل ویژگیهای برنامه نویسی Async را پیاده سازی کرده است و این بدین معنی است که عملیات میتوانند به صورت غیر همزمان اجرا شده و دیگر فعالیتها را بلوکه نکنند.
using Microsoft.AspNet.Identity; using Microsoft.AspNet.Identity.EntityFramework; using Microsoft.AspNet.Identity.Owin; using Microsoft.Owin; using Users.Models; namespace Users.Infrastructure { public class AppUserManager : UserManager<AppUser> { public AppUserManager(IUserStore<AppUser> store) : base(store) { } public static AppUserManager Create( IdentityFactoryOptions<AppUserManager> options, IOwinContext context) { AppIdentityDbContext db = context.Get<AppIdentityDbContext>(); AppUserManager manager = new AppUserManager(new UserStore<AppUser>(db)); return manager; } } }
زمانی که Identity نیاز به وهلهای از کلاس AppUserManager داشته باشد، متد استاتیک Create را صدا خواهد زد. این عمل زمانی اتفاق میافتد که عملیاتی بر روی دادههای کاربر انجام گیرد. برای ساخت وهلهای از کلاس AppUserManager نیاز به کلاس <UserStor<AppUser دارد. کلاس <UserStore<T یک پیاده سازی از رابط <IUserStore<T توسط EF میباشد که وظیفهی آن فراهم کنندهی پیاده سازی Storage-Specific متدهای تعریف شده در کلاس User Manager (که در اینجا AppUserManager ) میباشد. برای ساخت <UserStore<T نیاز به وهلهای از کلاس AppIdentityDbContext میباشد که از طریق Owin به صورت زیر قابل دریافت است:
AppIdentityDbContext db = context.Get<AppIdentityDbContext>();
یک پیاده سازی از رابط IOwinContext، به عنوان پارامتر به متد Create پاس داده میشود. در این پیاده سازی، یک تابع جنریک به نام Get تعریف شده که اقدام به برگشت وهله ای از اشیای ثبت شدهی در کلاس شروع Owin مینماید.
ساخت کلاس شروع Owin
اگر خاطرتان باشد یک کلید در قسمت AppSettings فایل Web.config به صورت زیر تعریف کردیم:
<add key="owin:AppStartup" value="Users.IdentityConfig" />
قسمت Value کلید بالا از دو قسمت تشکیل شده است: Users بیانگر فضای نام برنامهی شماست و IdentityConfig بیانگر کلاس شروع میباشد. (البته شما میتوانید هر نام دلخواهی را برای کلاس شروع بگذارید. فقط دقت داشته باشید که نام کلاس شروع و مقدار، با کلیدی که تعریف میکنید یکی باشد)
Owin مستقل از ASP.NET اعلام شده است و قراردادهای خاص خودش را دارد. یکی از این قراردادها تعریف یک کلاس و وهله سازی آن به منظور بارگذاری و پیکربندی میان افزار و انجام دیگر کارهای پیکربندی که نیاز است، میباشد. به طور پیش فرض این کلاس Start نام دارد و در پوشهی App_Start تعریف میشود. این کلاس حاوی یک متد به نام Configuration میباشد که بوسیله زیرساخت Owin فراخوانی میشود و یک پیاده سازی از رابط Owin.IAppBuilder به عنوان آرگومان به آن پاس داده میشود که کار پشتیبانی از Setup میان افزار مورد نیاز برنامه را برعهده دارد.
using Microsoft.AspNet.Identity; using Microsoft.Owin; using Microsoft.Owin.Security.Cookies; using Owin; using Users.Infrastructure; namespace Users { public class IdentityConfig { public void Configuration(IAppBuilder app) { app.CreatePerOwinContext<AppIdentityDbContext>(AppIdentityDbContext.Create); app.CreatePerOwinContext<AppUserManager>(AppUserManager.Create); app.UseCookieAuthentication(new CookieAuthenticationOptions { AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie, LoginPath = new PathString("/Account/Login"), }); } } }
رابط IAppBuilder بوسیله تعدادی متد الحاقی که در کلاسهایی که در فضای نام Owin تعریف شدهاند، تکمیل شده است. متد CreatePerOwinContext کار ساخت وهلهای از کلاس AppUserManager و کلاس AppIdentityDbContext را برای هر درخواست بر عهده دارد. این مورد تضمین میکند که هر درخواست، به یک دادهی تمیز از Asp.Net Identity دسترسی خواهد داشت و نگران اعمال همزمانی و یا کش ضعیف داده نخواهد بود. متد UseCookieAuthentication به Asp.Net Identity میگوید که چگونه از کوکیها برای تصدیق هویت کاربران استفاده کند که Optionهای آن از طریق کلاس CookieAuthenticationOptions مشخص میشود. مهمترین قسمت در اینجا پروپرتی LoginPath میباشد و مشخص میکند که کلاینتهای تصدیق هویت نشده، هنگام دسترسی به یک منبع محافظت شده، به کدام URL هدایت شوند که توسط یک رشته به متد PathString پاس داده میشود.
خوب دوستان برپایی سیستم Identity به پایان رسید. انشالله در قسمت بعدی به چگونگی استفادهی از این سیستم خواهیم پرداخت.
ObservableCollection در Entity Framework
public bool HasChanges() { return this.ChangeTracker.Entries().Any(e => e.State != EntityState.Unchanged); } public void RejectChanges() { foreach (var entry in this.ChangeTracker.Entries()) { switch (entry.State) { case EntityState.Modified: entry.State = EntityState.Unchanged; break; case EntityState.Added: entry.State = EntityState.Detached; break; } } }
var sqlDatabasePassword = pulumiConfig.RequireSecret("sql-server-nikola-dev-password"); var sqlDatabaseUserId = pulumiConfig.RequireSecret("sql-server-nikola-dev-user-id"); var resourceGroup = new ResourceGroup("rg-dds-nikola-dev", new ResourceGroupArgs { Name = "rg-dds-nikola-dev", Location = "WestUS" }); var storageAccount = new Account("storagenikoladev", new AccountArgs { Name = "storagenikoladev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, AccountKind = "StorageV2", AccountReplicationType = "LRS", AccountTier = "Standard", }); var container = new Container("container-nikola-dev", new ContainerArgs { Name = "container-nikola-dev", ContainerAccessType = "blob", StorageAccountName = storageAccount.Name }); var blobStorage = new Blob("blob-nikola-dev", new BlobArgs { Name = "blob-nikola-dev", StorageAccountName = storageAccount.Name, StorageContainerName = container.Name, Type = "Block" }); var appInsights = new Insights("app-insights-nikola-dev", new InsightsArgs { Name = "app-insights-nikola-dev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, ApplicationType = "web" // also general for mobile apps }); var sqlServer = new SqlServer("sql-server-nikola-dev", new SqlServerArgs { Name = "sql-server-nikola-dev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, AdministratorLogin = sqlDatabaseUserId, AdministratorLoginPassword = sqlDatabasePassword, Version = "12.0" }); var sqlDatabase = new Database("sql-database-nikola-dev", new DatabaseArgs { Name = "sql-database-nikola-dev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, ServerName = sqlServer.Name, RequestedServiceObjectiveName = "Basic" }); var appServicePlan = new Plan("app-plan-nikola-dev", new PlanArgs { Name = "app-plan-nikola-dev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, Sku = new PlanSkuArgs { Tier = "Shared", Size = "D1" } }); var appService = new AppService("app-service-nikola-dev", new AppServiceArgs { Name = "app-service-nikola-dev", ResourceGroupName = resourceGroup.Name, Location = resourceGroup.Location, AppServicePlanId = appServicePlan.Id, SiteConfig = new AppServiceSiteConfigArgs { Use32BitWorkerProcess = true, // X64 not allowed in shared plan! AlwaysOn = false, // not allowed in shared plan! Http2Enabled = true }, AppSettings = { { "ApplicationInsights:InstrumentationKey", appInsights.InstrumentationKey }, { "APPINSIGHTS_INSTRUMENTATIONKEY", appInsights.InstrumentationKey } }, ConnectionStrings = new InputList<AppServiceConnectionStringArgs>() { new AppServiceConnectionStringArgs { Name = "AppDbConnectionString", Type = "SQLAzure", Value = Output.Tuple(sqlServer.Name, sqlDatabase.Name, sqlDatabaseUserId, sqlDatabasePassword).Apply(t => { (string _sqlServer, string _sqlDatabase, string _sqlDatabaseUserId, string _sqlDatabasePassword) = t; return $"Data Source=tcp:{_sqlServer}.database.windows.net;Initial Catalog={_sqlDatabase};User ID={_sqlDatabaseUserId};Password={_sqlDatabasePassword};Max Pool Size=1024;Persist Security Info=true;Application Name=Nikola"; }) }, new AppServiceConnectionStringArgs { Name = "AzureBlobStorageConnectionString", Type = "Custom", Value = Output.Tuple(storageAccount.PrimaryAccessKey, storageAccount.Name).Apply(t => { (string _primaryAccess, string _storageAccountName) = t; return $"DefaultEndpointsProtocol=https;AccountName={_storageAccountName};AccountKey={_primaryAccess};EndpointSuffix=core.windows.net"; }) } } }); appService.OutboundIpAddresses.Apply(ips => { foreach (string ip in ips.Split(',')) { new FirewallRule($"app-srv-{ip}", new FirewallRuleArgs { Name = $"app-srv-{ip}", EndIpAddress = ip, ResourceGroupName = resourceGroup.Name, ServerName = sqlServer.Name, StartIpAddress = ip }); } return (string?)null; });
اگرچه ایجاد شیء و مقداردهی به ویژگیهای آن ما را به هدفمان میرساند، اما بهترین روش نیست چرا که ممکن است مقداردهی به یک ویژگی فراموش شده و سبب شود شیء در وضعیت نادرستی قرار گیرد. این مشکل با استفاده از سازندهها (Constructors) حل میشود.
سازنده (Constructor) عضو ویژه ای از کلاس است بسیار شبیه به یک متد که در هنگام وهله سازی (ایجاد یک شیء از کلاس) به صورت خودکار فراخوانی و اجرا میشود. وظیفه سازنده مقداردهی به ویژگیهای عمومی و خصوصی شیء تازه ساخته شده و به طور کلی انجام هر کاری است که برنامهنویس در نظر دارد پیش از استفاده از شیء به انجام برساند.
مثالی که در این بخش بررسی میکنیم کلاس مثلث است. برای پیاده سازی این کلاس سه ویژگی در نظر گرفته ایم. قاعده، ارتفاع و مساحت. بله مساحت را این بار به جای متد به صورت یک پروپرتی پیاده سازی میکنیم. اگرچه در آینده بیشتر راجع به چگونگی انتخاب برای پیاده سازی یک عضو کلاس به صورت پروپرتی یا متد بحث خواهیم کرد اما به عنوان یک قانون کلی در نظر داشته باشید عضوی که به صورت منطقی به عنوان داده مطرح است را به صورت پروپرتی پیاده سازی کنید. مانند نام دانشجو. از طرفی اعضایی که دلالت بر انجام عملی دارند را به صورت متد پیاده سازی میکنیم. مانند متد تبدیل به نوع داده دیگر. (مثلاً ()Object.ToString)
public class Triangle { private int _height; private int _baseLength; public int Height { get { return _height; } set { if (value < 1 || value > 100) { // تولید خطا } _height = value; } } public int BaseLength { get { return _baseLength; } set { if (value < 1 || value > 100) { // تولید خطا } _baseLength = value; } } public double Area { get { return _height * _baseLength * 0.5; } } }
• در اکسسور set دو ویژگی قاعده و ارتفاع، محدوده مجاز مقادیر قابل انتساب را بررسی نموده ایم. در صورتی که مقداری خارج از محدوده یاد شده برای این ویژگیها تنظیم شود خطایی را ایجاد خواهیم کرد. شاید برای برنامه نویسانی که تجربه کمتری دارند زیاد روش مناسبی به نظر نرسد. اما این یک روش قابل توصیه است. مواجه شدن کد مشتری (کد استفاده کننده از کلاس) با یک خطای مهلک که علت رخ دادن خطا را نیز میتوان به همراه آن ارائه کرد بسیار بهتر از بروز خطاهای منطقی در برنامه است. چون رفع خطاهای منطقی بسیار دشوارتر است. در مطالب آینده راجع به تولید خطا و موارد مرتبط با آن بیشتر صحبت میکنیم.
• در مورد ویژگی مساحت، اکسسور set را پیاده سازی نکرده ایم تا این ویژگی را به صورت فقط خواندنی ایجاد کنیم.
وقتی شیء ای از یک کلاس ایجاد میشود، بلافاصله سازنده آن فراخوانی میگردد. سازندهها هم نام کلاسی هستند که در آن تعریف میشوند و معمولاً اعضای داده ای شیء جدید را مقداردهی میکند. همانطور که میدانید وهله سازی از یک کلاس با عملگر new انجام میشود. سازنده کلاس بلافاصله پس از آنکه حافظه برای شیء در حال تولید اختصاص داده شد، توسط عملگر new فراخوانی میشود.
سازنده پیش فرض
سازندهها مانند متدهای دیگر میتوانند پارامتر دریافت کنند. سازنده ای که هیچ پارامتری دریافت نمیکند سازنده پیش فرض (Default constructor) نامیده میشود. سازنده پیش فرض زمانی اجرا میشود که با استفاده از عملگر new شیء ای ایجاد میکنید اما هیچ آرگومانی را برای این عملگر در نظر نگرفته اید.اگر برای کلاسی که طراحی میکنید سازنده ای تعریف نکرده باشید کامپایلر سی شارپ یک سازنده پیش فرض (بدون پارامتر) خواهد ساخت. این سازنده هنگام ایجاد اشیاء فراخوانی شده و مقدار پیش فرض متغیرها و پروپرتیها را با توجه به نوع آنها تنظیم مینماید. مثلاً مقدار صفر برای متغیری از نوع int یا false برای نوع bool و null برای انواع ارجاعی که در آینده در این مورد بیشتر خواهید آموخت.
اگر مقادیر پیش فرض برای متغیرها و پروپرتیها مناسب نباشد، مانند مثال ما، سازنده پیش فرض ساخته شده توسط کامپایلر همواره شیء ای میسازد که وضعیت صحیحی ندارد و نمیتواند وظیفه خود را انجام دهد. در این گونه موارد باید این سازنده را جایگزین نمود.
جایگزینی سازنده پیش فرض ساخته شده توسط کامپایلر
افزودن یک سازنده صریح به کلاس بسیار شبیه به تعریف یک متد در کلاس است. با این تفاوت که:- سازنده هم نام کلاس است.
- برای سازنده نوع خروجی در نظر گرفته نمیشود.
در مثال ما محدوده مجاز برای قاعده و ارتفاع مثلث بین ۱ تا ۱۰۰ است در حالی که سازنده پیش فرض مقدار صفر را برای آنها تنظیم خواهد نمود. پس برای اینکه مطمئن شویم اشیاء مثلث ساخته شده از این کلاس در همان بدو تولید دارای قاعده و ارتفاع معتبری هستند سازنده زیر را به صورت صریح در کلاس تعریف میکنیم تا جایگزین سازنده پیش فرضی شود که کامپایلر خواهد ساخت و به جای آن فراخوانی گردد.
public Triangle() { _height = _baseLength = 1; }
اجرای سازنده
همانطور که گفته شد سازنده اضافه شده به کلاس جایگزین سازنده پیش فرض کامپایلر شده و در هنگام ایجاد یک شیء جدید از کلاس مثلث توسط عملگر new اجرا میشود. برای بررسی اجرا شدن سازنده به سادگی میتوان کدی مشابه مثال زیر را نوشت.Triangle triangle = new Triangle(); Console.WriteLine(triangle.Height); Console.WriteLine(triangle.BaseLength); Console.WriteLine(triangle.Area);
سازندههای پارامتر دار
در مثال قبل یک سازنده بدون پارامتر را به کلاس اضافه کردیم. این سازنده تنها مقادیر پیش فرض مناسبی را تنظیم میکند. بدیهی است پس از ایجاد شیء در صورت نیاز میتوان مقادیر مورد نظر دیگر را برای قاعده و ارتفاع تنظیم نمود. اما برای اینکه سازنده بهتر بتواند فرآیند وهله سازی را کنترل نماید میتوان پارامترهایی را به آن افزود. افزودن پارامتر به سازنده مانند افزودن پارامتر به متدهای دیگر صورت میگیرد. در مثال زیر سازنده دیگری تعریف میکنیم که دارای دو پارامتر است. یکی قاعده و دیگری ارتفاع. به این ترتیب در حین فرآیند وهله سازی میتوان مقادیر مورد نظر را منتسب نمود.public Triangle(int height, int baseLength) { Height = height; BaseLength = baseLength; }
سازنده اخیر را میتوان به صورت زیر با استفاده از عملگر new و فراهم کردن آرگومانهای مورد نظر مورد استفاده قرار داد.
Triangle triangle = new Triangle(5, 8); Console.WriteLine(triangle.Height); Console.WriteLine(triangle.BaseLength); Console.WriteLine(triangle.Area);
در مطالب بالا چندین بار از سازندهها صحبت کردیم و گفتیم سازنده دیگری به کلاس اضافه میکنیم. این دو نکته را به خاطر داشته باشید:
- یک کلاس میتواند دارای چندین سازنده باشد که بر اساس آرگومانهای فراهم شده هنگام وهله سازی، سازنده مورد نظر انتخاب و اجرا میشود.
- الزامی به تعریف یک سازنده پیش فرض (به معنای بدون پارامتر) نیست. یعنی یک کلاس میتواند هیچ سازنده بی پارامتری نداشته باشد.
در بخشهای بعدی مطالب بیشتری در مورد سازندهها و سایر اعضای کلاس خواهید آموخت.
Mini Course #1 Clean Architecture + CQRS - YouTube
00:00:00 - Intro
00:01:10 - Why do we record this course?
00:04:39 - Layered architecture: Why I don't like it anymore?
00:27:27 - Clean architecture
00:38:16 - Course prerequisites (still can't pronounce it correctly)
00:42:30 - What do we build?
00:45:18 - *Domain Layer*
00:49:57 - Entity
01:00:20 - Primitive obsession code smell
01:03:20 - Value Object
01:10:54 - Custom exceptions
01:33:25 - Domain model validation
01:38:55 - Aggregate
01:50:50 - Domain event
02:14:30 - Factory
02:28:50 - Policy
02:43:50 - Repository
02:48:55 - Domain Layer: Summary
02:56:45 - CQS: Command Query Separation
03:07:46 - CQRS: Command Query Responsibility Segregation
03:29:48 - *Application Layer*
03:30:25 - Command/Command Handler/Command Dispatcher definitions
03:40:45 - Automatic command handlers registration
03:44:12 - Application & Domain registration
03:50:52 - Command
03:55:31 - Command Handler
04:01:11 - Where to put methods related to reading?
04:07:00 - Read Service
04:14:14 - Weather Service
04:28:25 - Overview of the other commands & command handlers
04:34:35 - Query/Query Handler/Query Dispatcher definitions
04:44:37 - Automatic query handlers registration (BONUS: example of copy/paste pattern)
04:52:50 - How to tackle reading on Query side?
05:06:30 - *Infrastructure Layer*
05:07:56 - Configuration of Entity Framework Core
05:15:15 - Read Models
05:19:05 - ReadDbContext & WriteDbContext
05:24:13 - EF Entity Configuration
05:42:37 - DbContexts registration
05:55:51 - Query Handlers
06:12:18 - Repository implementation on top of EF Core
06:16:45 - Read Service implementation on top of EF Core
06:19:00 - Dummy Weather Service implementation
06:23:20 - EF Migration
06:29:50 - Applying EF migrations automatically
06:39:38 - *Presentation Layer*
06:41:35 - Controller
06:50:43 - How to return ID of resource in CQRS approach?
07:00:02 - Testing API
07:09:29 - *Cross Cutting Concerns*
07:09:30 - Error Handling
07:20:49 - Logging
07:34:57 - *Unit Testing*
07:42:34 - Unit Test on Domain Layer
08:09:45 - Unit Test on Application Layer
08:34:00 - Summary
The previews of EF Core 5.0 require .NET Standard 2.1. This means:
- EF Core 5.0 runs on .NET Core 3.1; it does not require .NET 5.
- This may change in future previews depending on how the plan for .NET 5 evolves.
- EF Core 5.0 runs on other platforms that support .NET Standard 2.1.
- EF Core 5.0 will not run on .NET Standard 2.0 platforms, including .NET Framework.
Blazor 5x
مبانی Blazor
- قسمت 4 : بخش 1 - Data Binding
- قسمت 5 : بخش 2 - کامپوننتها
- قسمت 6 : بخش 3 - چرخههای حیات کامپوننتها
- قسمت 7 : بخش 4 - انتقال اطلاعات از کامپوننتهای فرزند به کامپوننت والد
- قسمت 8 : بخش 5 - تامین محتوای نمایشی کامپوننتهای فرزند توسط کامپوننت والد
- قسمت 9 : بخش 6 - ساده سازی تعاریف ویژگیهای المانها و انتقال پارامترها به چندین زیر سطح
- قسمت 10 : بخش 7 - مسیریابی
- قسمت 11 : بخش 8 - کار با جاوا اسکریپت
- قسمت 12 : بخش 9 - یک تمرین
کار با فرمها
- قسمت 13 : بخش 1 - کار با EF Core در برنامههای Blazor Server
- قسمت 14 : بخش 2 - تعریف فرمها و اعتبارسنجی آنها
- قسمت 15 : بخش 3 - ویرایش اطلاعات
- قسمت 16 : بخش 4 - تهیه سرویسهای آپلود تصاویر
- قسمت 17 : بخش 5 - آپلود تصاویر
- قسمت 18 : بخش 6 - حذف اطلاعات
- قسمت 19 : بخش 7 - نکات ویژهی کار با EF-Core در برنامههای Blazor Server
- قسمت 20 : بخش 8 - استفاده از یک کامپوننت ثالث HTML Editor
- قسمت 21 : بخش 1 - افزودن قالب ابتدایی Identity
- قسمت 22 : بخش 2 - ورود به سیستم و خروج از آن
- قسمت 23 : بخش 3 - کار با نقشهای کاربران
- قسمت 26 : ایجاد و تنظیمات اولیه
- قسمت 27 : کار با سرویسهای Web API
- قسمت 28 : نمایش لیست اطلاعات دریافتی از Web API
- قسمت 29 : یک تمرین: رزرو کردن یک اتاق انتخابی
- قسمت 30 : افزودن پرداخت آنلاین توسط درگاه مجازی پرباد
- قسمت 31 : بخش 1 - انجام تنظیمات اولیه
- قسمت 32 : بخش 2 - ثبت نام، ورود به سیستم و خروج از آن
- قسمت 33 : بخش 3 - بهبود تجربهی کاربری عدم دسترسیها
- پیاده سازی سیاستهای دسترسی پویای سمت سرور و کلاینت در برنامههای Blazor WASM
- قسمت اول : معرفی SSR
- قسمت دوم : بررسی حالت رندر سمت سرور
- قسمت سوم : روش ارتقاء برنامههای Blazor Server قدیمی به دات نت 8
- قسمت چهارم : معرفی فرمهای جدید تعاملی
- قسمت پنجم : امکان تعریف جزیرههای تعاملی Blazor Server
- قسمت ششم : نکات تکمیلی ویژگی راهبری بهبود یافتهی صفحات SSR
- قسمت هفتم : امکان تعریف جزیرههای تعاملی Blazor WASM
- قسمت هشتم : مدیریت انتقال اطلاعات Pre-Rendering سمت سرور، به جزایر تعاملی
- قسمت نهم : معرفی حالت رندر تعاملی خودکار
- قسمت دهم : مدیریت حالت کاربران در روشهای مختلف رندر
- قسمت یازدهم : قالب جدید پیاده سازی اعتبارسنجی و احراز هویت - بخش اول
- قسمت دوازدهم : قالب جدید پیاده سازی اعتبارسنجی و احراز هویت - بخش دوم
- قسمت سیزدهم : امکان تعریف Sections
- قسمت چهاردهم : امکان استفاده از کامپوننتهای Blazor در برنامههای ASP.NET Core 8x
- Best practiceهای یک پروژه Blazor
- کامپوننتهای جنریک در Blazor
- مدیریت حالت در برنامههای Blazor توسط الگوی Observer - قسمت اول
- مدیریت حالت در برنامههای Blazor توسط الگوی Observer - قسمت دوم
- روش آپلود فایلها به همراه اطلاعات یک مدل در برنامههای Blazor WASM 5x
- دریافت و نمایش فایلهای PDF در برنامههای Blazor WASM
- ارسال خطاهای رخدادهی در برنامههای سمت کلاینت Blazor WASM، به تلگرام
- بهبود کارآیی نمایش لیستها در Blazor با استفاده از دایرکتیو key@
- معرفی Blazor Hybrid
- امکان استفاده از کتابخانههای native در Blazor WASM 6x
- امکان ساخت برنامههای دسکتاپ چندسکویی Blazor در دات نت 6
- حل مشکل بارگذاری اولیه دستورات جاوا اسکریپتی در پروژههای Blazor
- پیاده سازی Remote Validation در Blazor
- روش ایجاد پروژههای کتابخانهای کامپوننتهای Blazor
- استفاده از date picker شمسی جاوا اسکریپتی در Blazor با قابلیت ورود تاریخ به صورت دستی
- ارائهی قالبی عمومی برای استفاده از تقویمهای جاوااسکریپتی در Blazor
- بهبود صفحهی بارگذاری اولیه در Blazor WASM
- استفاده از چند دکمه با عملکردهای مختلف برای ارسال یک EditForm در Blazor
- دستیابی به HttpContext در Blazor Server
- جلوگیری از دوباره اجرا شدن ناخواستهی متدهای نامتقارن در Blazor
- نکات ویژه کار با عملیات نامتقارن در Blazor Server
- استفاده از لنگر (anchor) برای اسکرول به قسمت خاصی از صفحه در Blazor Server