<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>پست‌های انتشارات دات نت زوم</title>
        <link>https://virgool.io/dotnetzoom/feed</link>
        <description>? Everything about .NET - آموزش تخصصی دات نت</description>
        <language>fa</language>
        <pubDate>2026-04-15 08:22:48</pubDate>
        <image>
            <url>https://files.virgool.io/upload/publication/dwsxc7tdvjth/avxle4.png</url>
            <title>دات نت زوم</title>
            <link>https://virgool.io/dotnetzoom</link>
        </image>

                    <item>
                <title>ساخت چت روم با Blazor Web Assembly و SignalR قسمت دوم: ساخت کلاینت</title>
                <link>https://virgool.io/dotnetzoom/%D8%B3%D8%A7%D8%AE%D8%AA-%DA%86%D8%AA-%D8%B1%D9%88%D9%85-%D8%A8%D8%A7-blazor-web-assembly-%D9%88-signalr-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-%D8%B3%D8%A7%D8%AE%D8%AA-%DA%A9%D9%84%D8%A7%DB%8C%D9%86%D8%AA-nfwjuzeuqzbf</link>
                <description>در این مقاله قصد داریم به ادامه ساخت چت روم با ASP Net Core Web API و Blazor Web Assembly بپردازیم.ابتدا به سراغ ساخت صفحه لاگین و رجیستر میرویم. در پوشه Pages یک razor component تحت عنوان Login میسازیم. سپس برای داشتن css isolation یک فایل دیگر با نام Login.razor.css ایجاد میکنیم. برای اینکه css isolation به درستی کار کند، قطعه کد زیر را در فایل index.html به قسمت head اضافه میکنیم. https://gist.github.com/babaktaremi/614f0483aee9a5d436907841e78fc173 قسمت ChatApplication.client در واقع نام assembly پروژه میباشد. سپس فایل های استاتیک شامل Font Awsome و Bootstrap را به index.html اضافه میکنیم. در حال حاضر index.html شامل کد های زیر می باشد. https://gist.github.com/babaktaremi/c5fb91a3ddd1764e6d9fcaaa94f5781b پروژه Client را اجرا میکنیم. به صفحه لاگین میرویم و مشاهده میکنیم که استایل ها به درستی لود شده انداستفاده از Local Storage در Blazorبرای استفاده از Access Token و ارتباط با Server نیاز داریم که توکن دریافتی را در جایی ذخیره کنیم. برای اینکار بهترین کار استفاده از قابلیت Local Storage در Browser می باشد. میتوانیم از قابلیت Javascript Interop در Blazor برای کار با Local Storage استفاده کنیم. اما پکیجی با نام Blazored.LocalStorage وجود دارد که کار با Local Storage در Blazor را بسیار راحت میکند. این پکیج را در پروژه Client نصب میکنیم.همچنین در Package Manager Console نیز میتوانیم با دستور زیر این پکیج را نصب کنیمInstall-Package Blazored.LocalStorage -Version 4.1.5سپس قطعه کد زیر را برای استفاده Local Storage به کلاس Program اضافه میکنیم. https://gist.github.com/babaktaremi/3148a94f25533ea35547b60c29536526 جداسازی Code از UIبرای اینکه کد تمیزتری داشته باشیم میتوانیم کد مربوط به صفحه UI را از منطق پروژه جدا کنیم. می دانیم که هرکدام از razor فایل ها پس از کامپایل به یک partial class تبدیل میشوند. با ایجاد فایل با پسوند razor.cs و هم نام با فایل razor اصلی میتوانیم به نوعی یک code behind برای هر صفحه razor داشته باشیم. پس فایل Login.razor.cs را به شکل زیر ایجاد میکنیم. https://gist.github.com/babaktaremi/4450e4fd79e3e0c615ba52fc20e3310a حال میتوانیم منطق پروژه Client و قسمت های مربوط به بخش Login را در این کلاس بنویسیم.ایجاد سرویس مربوط به Loginابتدا در پروژه Client یک پوشه با نام Service ایجاد میکنیم. در پوشه یک پوشه دیگر با نام Login ایجاد میکنیم.اینترفیس ILoginService را به شکل زیر ایجاد میکنیم. https://gist.github.com/babaktaremi/597e36d517deaa0229548c456a97b17a حال کلاس LoginService را که این اینترفیس را پیاده سازی میکند به شکل زیر مینویسیم https://gist.github.com/babaktaremi/5fedaf5a53efd772688628a7a04d5e14 ابتدا یک instance از HttpClient را تزریق میکنیم. از Extension Method ای به نام PostAsJsonAsync که مدل را به صورت JSON در Body به URL داده شده پست میکند استفاده میکنیم. اگر Response داده شده دارای Status Code 200 نبود null بر میگردانیم و در غیر این صورت Content آن را به صورت stream خوانده و به Access Token مدل را Deserialize میکنیم.برای آنکه HttpClient در سرویس ما تزریق شود، تکه کد زیر را در کلاس Program پروژه Client اضافه میکنیم. قبل از آن نیاز داریم که پکیج Microsoft.Extensions.Http را به پروژه  Client آضافه کنیم.همچنین میتوانیم از دستور زیر در Package Manager Console استفاده کنیم.Install-Package Microsoft.Extensions.Http -Version 5.0.0استفاده از Toaster برای اعلام Notification ها به کاربرمانند Local Storage ، برای استفاده از Toaster در Blazor نیز پکیج وجود دارد که نصب و تنظیم آن مانند پکیج قبلی کار بسیار سرراست و ساده ای است.ابتدا پکیج Sotsera.Blazor.Toaster را دانلود و نصب میکنیم.با استفاده از دستور زیر در Package Manager Console نیز میتوانیم این پکیج را نصب کنیم.Install-Package Sotsera.Blazor.Toaster -Version 3.0.0سپس در کلاس Program قطعه کد زیر را برای تنظیم Toaster اضافه میکنیم. https://gist.github.com/babaktaremi/a4f2ce0323b785186ea7d35dff3c7ef5 در اینجا Postion مربوط به Toaster را در قسمت بالا وسط تعیین کرده ایم. سپس به فایل index.html قطعه کد زیر را اضافه میکنیم. https://gist.github.com/babaktaremi/68c3c0586a066d97186978eddcc256b2 سپس به فایل App.razor در قسمت بالا قطعه کد زیر را اضافه میکنیم https://gist.github.com/babaktaremi/b02e38416dc5303833f8d34cbef73536 تکمیل کد های مربوط به بخش Loginحال که همه سرویس های مربوط به بخش Login آماده است به سراغ تکمیل آن می رویم. یادتان باشد که ما بخش Code را از UI جدا کرده ایم پس تمام کد های مربوط به بخش Login را در کلاس Login.razor.cs مینویسیم.ابتدا سرویس های مورد نیاز را به این بخش تزریق میکنیم https://gist.github.com/babaktaremi/b10ab8536eb23e60952fd5498f9a63db در اینجا سرویس های مربوط به Local Storage و Toaster و Login Service را تزریق کرده ایم. صفت Inject در واقع کار تزریق را برای ما در Blazor انجام میدهد. برای Navigate کردن به سایر صفحات نیز Navigation Manager را Inject میکنیمسپس یک پراپرتی از جنس LoginViewModel را new کرده و در کلاس قرار میدهیم. برای اینکه در تگ EditForm در Blazor این مدل قابل استفاده باشد نیاز است که آن را new کنیم. https://gist.github.com/babaktaremi/ed996bd3c75f84816160a60178573f00 حال متد OnInitializedAsync را override میکنیم. این متد زمانی که صفحه رندر و Initialize می شود اجرا میشود. https://gist.github.com/babaktaremi/f656fe021745b3930a54b5ef5668bfb4 در اینجا در Local Storage چک میکنیم که آیا کلیدی با نام userToken وجود دارد یا خیر. اگر وجود داشت به معنی این است که یوزر از قبل لاگین کرده است و آن را به صفحه اصلی میفرستیم.در قسمت UI برای اینکه فرم Login به درستی کار کند تغییرات زیر را انجام میدهیم https://gist.github.com/babaktaremi/30a6f0d1b3313bbd2f6df8830e4328e4 در خط 4 با استفاده از Component ای به نام DataAnnotationsValidator قابلیت Data Annotation Validation را به فرم اضافه میکنیم.در خط 8 و 12 با استفاده از InputText Componenet مقادیر را به پراپرتی های LoginViewModel بایند میکنیم و همچنین با استفاده از ValidationMessage پیامی که هنگام خالی بودن InputText باید نمایش داده شود را نمایش میدهیم. متد HandleValidSubmit را به شکل زیر پیاده سازی میکنیم. https://gist.github.com/babaktaremi/c2f35582b6fae0e50d73409257e91a9c اگر Token برابر null بود با Toaster به کاربر نشان میدهیم که کاربر یافت نشده است و در غیر این صورت مقدار Local Storage را برابر Access Token قرار میدهیم.قبل از هرچیزی کلاس AccessToken را به شکل زیر باید تغییر دهیم https://gist.github.com/babaktaremi/819ea908c598a7b2e74b5c1d084ad32d باید یک Constructor هنگام deserialize کردن مقدار Json در نظر بگیریم.حال فرم Login را چک میکنیم. پروژه را اجرا میکنیم. ابتدا مقادیر username و password را خالی می گذاریم و مشاهده میکنیم که validation ها درست کار میکنند.حال مقدار username و password را با مقادیر اشتباه پر میکنیم و مشاهده میکنیم که Toaster هم درست کار میکند.حال با مقادیر درست username و password را پر میکنیم. مشاهده میشود که در local storage مرورگر مقدار Token ذخیره شده است.به user_id برای جداسازی چت های کاربر و سایر کاربران نیاز داریم. در اینجا برای راحتی کار user_id را داخل توکن قرار داریم ولی این کار باعث بوجود آمدن مشکلات امنیتی میشود. پیشنهاد میکنم که از GUID برای اینکار استفاده کنید و یا راه حل دیگری برای تشخیص یوزر جاری در نظر بگیرید.به همین منوال میتوانیم صفحه مربوط به رجیستر و ثبت نام کاربر را نیز بسازیم.ساخت صفحه چت کاربراندر فولدر Pages یک razor Component به نام Index می سازیم. کد اولیه این صفحه به صورت زیر خواهد بود. https://gist.github.com/babaktaremi/9663b4e21c8bfb6c1c858d92418cacf6 نوشتن کد های مربوط به Chat و SignalR Clientابتدا پکیج مربوط به SignalR Client را نصب میکنیم.از طریق Package Manager Console نیز با دستور زیر میتوانیم SignalR را نصب کنیم.Install-Package Microsoft.AspNetCore.SignalR.Client -Version 5.0.9سپس سرویس مربوط به Chat را مینویسیم. در فولدر Service یک فولدر دیگر با نام Chat ایجاد میکنیم و در آن اینترفیس IChatService را به شکل زیر مینویسیم. https://gist.github.com/babaktaremi/c71258dc8df3bb191ee0084ba54c608d سپس کلاس ChatService را به که این اینترفیس را پیاده سازی میکند به شکل زیر مینویسیم. https://gist.github.com/babaktaremi/5ad6462f9ede4e36128f5d0f6b4b7d9a در خط 14 برای احراز هویت، توکن را به صورت bearer در Header قرار داده ایم.سپس در کلاس Program.cs این سرویس را به شکل زیر رجیستر میکنیم. https://gist.github.com/babaktaremi/5049a917b85f69f7109134e3216368f2 سپس کلاس Index.razor.cs را به شکل زیر میسازیم https://gist.github.com/babaktaremi/ac29a7b8877cb3ff39b71ef0ca60dea8 ابتدا باید از IAsyncDisposable ارث بری کنیم چرا که نیاز داریم در پایان طول عمر Component مقادیر Hub و Debounce Timer را Dispose کنیم.سپس در خط 5 یک فیلد از Hub Connection تعریف میکنیم.سپس برای اینکه یک تاخیر هنگام تایپ کاربر و Notify کردن سایر کاربران ایجاد کنیم یک Debounce Timer تعریف میکنیم که پس از هر 500 میلی ثانیه یک متد را صدا میزند.در خط 13 یک فیلد از جنس string تعریف میکنیم که مقدار پیام وروردی کاربر به آن bind می شود.در خط 14 یک فیلد از جنس int داریم که ID کاربر را در آن ذخیره میکنیم. از این فیلد بعد ها برای تمایز بین پیام های کاربر و سایر کاربران استفاده خواهیم کرد.در خط 15 یک لیست از جنس UserMessageViewModel داریم که تاریخچه چت را به کاربر نشان میدهد.از خط 19 تا 22 سرویس های مورد نیاز را تزریق میکنیم.کار اصلی از override کردن متد OnInitializedAsync شروع میشود. ابتدا بررسی میکنیم که آیا کاربر در Browser و Local Storage مقادیر توکن را دارد یا خیر و اگر نداشت، وی را به صفحه Login هدایت میکنیم. سپس فیلد _userId را با استفاده از متد GetUserIdAsync  که در خط 82 تعریف شده است از Local Storage خوانده و مقدار دهی میکنیم. سپس مقدار _chatHistory را بوسیله سرویسی که قبلا نوشتیم از Web API خوانده و مقدار دهی میکنیم. سپس event مربوطه به هنگامی که debounce timer مقدار 500 میلی ثانیه را سپری میکند را صدا میزند  مقدار دهی میکنیم. متد IsTyping فانکشن مربوطه را در SignalR Server فراخوانی میکند. سپس نوبت به مقداردهی _hub میرسد. در خط 39 مقادیر مربوط به URL را مقداردهی میکنیم. در خط 40 مقدار Access Token را بوسیله متد GetAccessTokenValueAsync از Local Storage خوانده و مقدار دهی میکنیم. سپس متد هایی که Hub روی آنها قرار است invoke بشود را تعریف میکنیم. ابتدا بوسیله Toaster هنگامی که کاربری به SignalR Server متصل میشود را نشان میدهیم. سپس متدی که هنگام دریافت پیام جدید صدا زده می شود را تعریف میکنیم. هنگام دریافت پیام جدید ، آن را به Chat History اضافه میکنیم و برای rerender کردن UI متد StateHasChanged را صدا میزنیم. سپس برای Notify کردن سایر کاربران هنگامی که یک یوزر تایپ میکند ، متد UpdateUserIsTypingAsync را می سازیم .متد InvokeAsync در خط 56 یک اکشن ورودی برای رندر کردن UI دریافت میکند. در آن مقدار typing user را از Server Hub دریافت کرده و برای نمایش آن سمت UI متد StateHasChanged را صدا میزنیم. پس از گذشت 1 ثانیه مقدار typing user را خالی کرده و مجددا StateHasChanged را صدا میزنیم . در نهایت در خط 48 Hub را استارت میکنیم.در خط 89 متدی که هنگام زدن دکمه ارسال باید فراخوانی بشود را تعریف میکنیم. اگر مقدار message خالی نبود آن را به متد OnNewMessage در SignalR Server میفرستیم. سپس مقدار message را خالی میکنیم و متد StateHasChanged را صدا میزنیم.در خط 101 یک متد برای on input event  تعریف میکنیم که وقتی کاربر در حال تایپ در Text Area است صدا زده میشود. در آن یک بار Debounce Timer را متوقف میکنیم و مجددا استارت میزنیم.در نهایت در خط 114 مقدار Hub و Timer را Dispose میکنیم.نوشتن کد های مربوط به UI صفحه چتفایل Index.razor را به شکل زیر بازنویسی میکنیم. https://gist.github.com/babaktaremi/3b874bf665d0c28db72d5276dabbb2b5 ابتدا در خط 6 چک میکنیم که اگر مقدار typing user خالی نبود ، آن را در قسمت بالای صفحه نمایش دهد.سپس بین چت های موجود در Chat History پیمایش میکنیم و مسیج های کاربر و سایر کاربرها را نمایش میدهیم.در خط 46 مقدار text area را به message بایند میکنیم و event مربوط به  را به متد InitiateUserIsTyping که قبلا تعریف کرده بودیم Assign میکنیم.در خط 47 نیز event مربوط به  که برای Button تعریف شده است را به متد SendMessage که قبلا تعریف کرده ایم Assign میکنیم.تست نهاییابتدا دو مرورگر باز میکنیم و در هر دو آنها Login میکنیم. سپس مشاهده میکنیم که نشان دادن کاربر آنلاین و همچنین ارسال پیام و نشان دادن کاربر در حال تایپ به درستی کار میکند.جمع بندیدر این پروژه یک سرور از صفر با استفاده از Identity و Signal R و همچنین JWT Authentication ساختیم. همچنین به طور اجمالی با Blazor web assembly و نحوه استفاده از JWT Token و ارتباط با Signal R آشنا شدیم. شدیدا توصیه میکنم که کد های مربوط به این مقاله را از گیت هاب دریافت کرده و آن را روی سیستم خود اجرا و دیباگ کنید. در نهایت اگر سوال یا نظری داشتید، خوشحال میشوم که آن را در بخش نظرات این مقاله مطرح کنید. https://github.com/babaktaremi/Blazor-Chat-Application مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Sat, 04 Sep 2021 15:25:21 +0430</pubDate>
            </item>
                    <item>
                <title>ساخت چت روم با Blazor Web Assembly و SignalR قسمت اول: ساخت سرور</title>
                <link>https://virgool.io/dotnetzoom/%D8%B3%D8%A7%D8%AE%D8%AA-%DA%86%D8%AA-%D8%B1%D9%88%D9%85-%D8%A8%D8%A7-blazor-web-assembly-%D9%88-signalr-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-%D8%B3%D8%A7%D8%AE%D8%AA-%D8%B3%D8%B1%D9%88%D8%B1-anfpjcdmdbn9</link>
                <description>در این مقاله دو قسمتی قصد داریم که بوسیله SignalR در سرور ASP Net Core و Blazor Web Assembly یک چت روم بسازیم.معماری کلی پروژهبرای ساده نگه داشتن پروژه، یک پروژه سرور که ASP Net Core Web Api هست داریم. برای کلاینت یک پروژه Blazor Web Assembly داریم و برای مشترکات بین دو پروژه که شامل مدل ها  هست یک پروژه از نوع Class Libraryداریم. معماری کلی پروژه را میتوانید در شکل زیر مشاهده کنید. دقت داشته باشید که دو پروژه Server و Client به پروژه Shared رفرنس دارند.ساخت و تکمیل سرورافزودن Identity به سرورابتدا پکیج Microsoft.Extensions.Identity.Stores را از طریق Nuget دانلود و نصب میکنیماز طریق Package Manager نیز میتوانیم با دستور زیر اقدام به نصب این پکیج بکنیم.Install-Package Microsoft.Extensions.Identity.Stores -Version 5.0.9سپس به سراغ نصب پکیج Microsoft.AspNetCore.Identity.EntityFrameworkCore می رویم از طریق Package Manager نیز میتوانیم با دستور زیر اقدام به نصب این پکیج بکنیم.Install-Package Microsoft.AspNetCore.Identity.EntityFrameworkCore -Version 5.0.9ساخت مدل های Identityپس از نصب Identity به سراغ ساخت مدل های آن می رویم. ابتدا در پروژه سرور یک فولدر با نام IdentityModels ایجاد میکنیمبرای ساخت مدل User به شکل زیر عمل میکنیم https://gist.github.com/babaktaremi/d26815fdf353aa639b6c1e646d3757a5 سپس سایر مدل های Identity را به شکل زیر میسازیم https://gist.github.com/babaktaremi/0824da8ba22e90abc7929c853e8c987e  https://gist.github.com/babaktaremi/47feb0145ce818c7815d5d5df8ff1102  https://gist.github.com/babaktaremi/73b60523331c32202bacdece1c8dc5c1  https://gist.github.com/babaktaremi/eb10695641d9c99fd07c18548aa9b35b  https://gist.github.com/babaktaremi/9ee3a6d30eee6a9afa83f6228d40f780  https://gist.github.com/babaktaremi/d91fdecd626f11fc8d07f7bc2999a791 ساخت Database Contextپس از ساخت مدل های Identity به سراغ ساخت ApplicationDbContext می رویم و آن را به شکل زیر می سازیم.ابتدا یک فولدر با نام Context می سازیم و ApplicationDbContext را داخل آن قرار میدهیم. https://gist.github.com/babaktaremi/305485da35f7387d464af034b8f68a3e برای استفاده از EF Core در پروژه و ایجاد Migration ها نیازمند پکیج های زیر هستیم که از طریق Package Manager و با دستورات زیر آنها را نصب میکنیمInstall-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.9Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.9Install-Package Microsoft.EntityFrameworkCore.Tools -Version 5.0.9سپس در متد ConfigureServices کلاس Startup این Database Context را رجیستر میکنیم. https://gist.github.com/babaktaremi/b2020dcedb9dd9b6622270721d264fd2 سپس با ایجاد Migration جداول مربوطه را میسازیم.تنظیم Identityبرای تنظیم Identity به شکل زیر عمل میکنیم.ابتدا یک فولدر با نام Extensions می سازیم و سپس یک کلاس داخل آن با نام IdentityConfigurationExtension ایجاد میکنیم. https://gist.github.com/babaktaremi/e557e618af94920a32a2cf497c78b79e در اینجا تنها یک پیاده سازی برای User Claim ها داریم که در آن میتوانیم Custom Claim ها را به توکن کاربر اضافه کنیم. پیاده سازی آن به شکل زیر می باشد https://gist.github.com/babaktaremi/0972dc5eabbaf975584678f9e9e280cb سپس در متد ConfigureServices کلاس Startup از این کلاس به شکل زیر استفاده میکنیم. https://gist.github.com/babaktaremi/0f2d47564bb428d71e9080c1f7fac465 افزودن JWT به Identityبرای افزودن JWT به Identity نیازمند پکیج Microsoft.AspNetCore.Authentication.JwtBearer هستیم این پکیج را از طریق دستور زیر نیز میتوانیم نصب کنیم.Install-Package Microsoft.AspNetCore.Authentication.JwtBearer -Version 5.0.9برای تنظیم JWT در فولدر Extensions یک کلاس با نام JWTConfigurationExtension به شکل زیر ایجاد میکنیم. https://gist.github.com/babaktaremi/d1daabdeaae4c3a579bc2f6f7e979eb6 سپس در متد ConfigureServices کلاس Startup تغییرات زیر را اعمال میکنیم. https://gist.github.com/babaktaremi/40b72abfc4814b3c589b0ae3f3a27438 در متد Configure کلاس Startup نیز تغییرات زیر را اعمال میکنیم. https://gist.github.com/babaktaremi/dabef9c1c2432d34484e1837a47b36eb ایجاد سرویس های مربوط به ساخت JWT Tokenبرای ایجاد JWT Token ابتدا در پروژه Shared کلاس AccessToken را به شکل زیر میسازیم. ابتدا در پروژه Shared یک پوشه با نام JWT ایجاد میکنیم و در آن کلاس AccessToken را قرار میدهیم. برای استفاده از کلاس JwtSecurityToken نیازمند پکیج System.IdentityModel.Tokens.Jwt که آن را با دستور زیر از طریق Package Manager Console نصب میکنیم.Install-Package System.IdentityModel.Tokens.Jwt -Version 6.12.2 https://gist.github.com/babaktaremi/349a70882d9404fd8ee7d0386aeeeff2 این کلاس شامل اطلاعاتی است که برای احراز هویت کاربران مورد نیاز است. ابتدا تایپ توکن را بعنوان Bearer تعریف میکنیم و سپس مقدار زمانی که توکن معتبر است را بعنوان ثانیه نشان میدهیم. در پروژه Server یک فولدر با نام Service ایجاد میکنیم و در آن یک فولدر دیگر با نام Jwt ایجاد میکنیم.سپس در آن یک اینترفیس با نام IJwtService ایجاد میکنیم که به صورت زیر میباشد. https://gist.github.com/babaktaremi/eb19489a03f57b3dd649c04f7d362577 سپس کلاس JwtService را به شکل زیر که اینترفیس IJwtService را پیاده سازی میکند مینویسیم. https://gist.github.com/babaktaremi/6a473e9e0b96e31008787c6268acca57 در این کد ما JWT Token را به صورت Encrypt شده در میاوریم. برای مطالعه بیشتر درباره نحوه Encrypt کردن JWT Token میتوانید به این مقاله از محمد جواد ابراهیمی عزیز مراجعه کنید.سپس در متد ConfigureServices این سرویس را به شکل زیر رجیستر میکنیم. https://gist.github.com/babaktaremi/7c5a1d7db7e9223b9fbdb52df366a5db ایجاد اکشن های Login و Register ابتدا مدل های Login و Register را میسازیم. در پروژه Shared یک پوشه با نام Account ایجاد میکنیم و دو کلاس LoginViewModel و RegisterViewModel را به شکل زیر میسازیم. https://gist.github.com/babaktaremi/0d92076e8de6d0687d67f9203ac0e78e  https://gist.github.com/babaktaremi/c4210bee5b68f711a334e127c7688b34 سپس به سراغ ساخت AccountController می رویم. ابتدا این کنترلر را به شکل زیر می سازیم و سپس اکشن های Login و Register را به آن اضافه میکنیم. https://gist.github.com/babaktaremi/54451345e7c880b40630c2af3890e5b3 برای ساخت اکشن Register به شکل زیر عمل میکنیم https://gist.github.com/babaktaremi/963b1de8a3e1361d132169092094faa0 برای ساخت اکشن Login نیز به شکل زیر عمل میکنیم. https://gist.github.com/babaktaremi/0e770471df62788851f21fc7430bf498 سپس این دو اکشن را در پنل Swagger تست میکنیم.ساخت جدول Chat  Historyابتدا در پروژه Server یک فولدر با نام Models ایجاد میکنیم. و در آن یک کلاس با نام ChatHistory ایجاد میکنیم. این جدول رابطه یک به چند با جدول User دارد. https://gist.github.com/babaktaremi/75e117772d096d7bf918172ca24cac82 سپس تغییر زیر را در کلاس User ایجاد میکنیم. https://gist.github.com/babaktaremi/d81b8f19d063b695b6cb88b5e3416c86 سپس تغییر زیر در کلاس ApplicationDbContext ایجاد میکنیم. https://gist.github.com/babaktaremi/7a409bdaf33a8ca827a80ad10e42d7d0 سپس Migration مربوط به ایجاد جدول ChatHistory را ایجاد میکنیم و در نهایت دیتابیس را آپدیت میکنیم.ساخت Chat Hub و Chat Controllerابتدا در پروژه Shared یک فولدر با نام Chat ایجاد میکنیم و کلاس های مربوط به Chat Hub را در آن ایجاد میکنیم.در این فولدر 3 کلاس با نام های UserMessageViewModel و UserIsTypingViewModel و UserJoinedChatViewModel و UserNewMessageViewModel قرار دارند.از UserMessageViewModel برای نشان دادن پیام ها و همچنین Notify کردن سایر کاربران هنگام ایجاد پیام جدید استفاده میکنیم.از UserIsTypingViewModel برای Notify کردن سایر کاربران هنگامی که یک کاربر در حال تایپ است استفاده میکنیماز UserJoinedChatViewModel برای Notify کردن سایر کاربران هنگامی که یک کاربر به چت روم وصل میشود استفاده میکنیم.از UserNewMessageViewModel هنگامی که کاربر یک پیام جدید به سرور میفرستد استفاده میکنیم. https://gist.github.com/babaktaremi/cf0ed0347ce9afcd73c4a93fff2cca08  https://gist.github.com/babaktaremi/0b06acd0092b5d697d9507a14282b09f  https://gist.github.com/babaktaremi/c8c0bb4e0ac072b97c3f3929c5ae86b4  https://gist.github.com/babaktaremi/cdda68f7f469a0deeae997560aff304b سپس در پروژه Server یک پوشه با نام Hub ایجاد میکنیم. در آن یک اینترفیس با نام IChatHub به شکل زیر اضافه میکنیم. https://gist.github.com/babaktaremi/d00ad7ac5001e2e827202851711fa338 سپس ChatHub را به شکل زیر ایجاد میکنیم. https://gist.github.com/babaktaremi/be32491c841dd4259bd1e59c9a6a0fb3 متد OnConnectedAsync را Override کرده ایم و در خط 8 به محض اینکه یک کاربر به هاب متصل میشود نام کاربری آن را به سایر کلاینت ها نشان میدهیم.سپس به Hub یک متد با نام OnNewMessage اضافه میکنیم که پیام های جدید را گرفته، به دیتابیس اضافه کرده و سپس سایر کلاینت ها را Notify کند. https://gist.github.com/babaktaremi/b4c2c7f411a88596fe6866a92f59599a سپس برای اینکه به سایر کاربران نشان دهیم که کاربر در حال تایپ است متد زیر به هاب اضافه میکنیم. https://gist.github.com/babaktaremi/f7f4b49ef4da631e79eed6e6897b5d4e در متد ConfigureServices کلاس Startup سیگنال آر را به شکل زیر به پروژه اضافه میکنیم. https://gist.github.com/babaktaremi/9f0a97ebb7cc1c420adf67456aace61b سپس ChatHub را به شکل زیر به URL مپ میکنیم. https://gist.github.com/babaktaremi/ccaff63cf6a8d476b19c2b4614b27648 در کلاس JwtConfigurationExtension برای اینکه ChatHub بتواند با JWTکار کند باید تغییرات زیر را اعمال کنیم. چون که در Web Socket نمیتوان مقدار AccessToken را از Header خواند، باید آن را تحت یک QueryString به سرور پاس دهیم. پس کلاس JwtConfigurationExtension به شکل زیر در میاید. https://gist.github.com/babaktaremi/6140381ba56ba3b6b46557d186450bc3  حال به سراغ ساخت ChatController میرویم. شکل ابتدایی ChatController به صورت زیر است. https://gist.github.com/babaktaremi/367f822f404ddefad89d302f65c4d901 در این کنترلر ما نیاز به یک اکشن داریم که پیام هایی که تا به حال منتشر شده است را دریافت کند https://gist.github.com/babaktaremi/01622cc87290d2cb479176484b7466a5 تنظیمات CORS برای اینکه Web Api ساخته شده سمت Client در دسترس باشد، نیاز داریم که CORS را در آن فعال کنیم. برای فعال سازی CORS به شکل زیر عمل میکنیم.ابتدا در متد ConfigureServices کلاس Startup قطعه کد زیر را اضافه میکنیم https://gist.github.com/babaktaremi/e4e0433b1dc8a7ffcbd010b0761665f9 برای اینکه بتوانیم از JWT Token در SignalR استفاده کنیم باید از متد AllowCredentials استفاده کنیم. سپس در متد Configure کلاس Startup قطعه کد زیر را اضافه میکنیم.جمع بندیدر این قسمت از مقاله یک سرور برای اپلیکیشن چت روم با استفاده از Identity ، JWT Authentication و SignalR ساختیم. در قسمت بعد به سراغ ساخت بخش کلاینت با استفاده از Blazor Web Assembly و Signal R Client می رویم و در نهایت یک اپلیکیشن چت روم به صورت Real-Time خواهیم داشتمقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Wed, 01 Sep 2021 12:22:39 +0430</pubDate>
            </item>
                    <item>
                <title>C# 9.0: Records - کار با داده‌های تغییر ناپذیر کلاس‌ها</title>
                <link>https://virgool.io/dotnetzoom/c-90-records-%DA%A9%D8%A7%D8%B1-%D8%A8%D8%A7-%D8%AF%D8%A7%D8%AF%D9%87-%D9%87%D8%A7%DB%8C-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1-%D9%86%D8%A7%D9%BE%D8%B0%DB%8C%D8%B1-%DA%A9%D9%84%D8%A7%D8%B3-%D9%87%D8%A7-bxiisfdwxc4q</link>
                <description>در نوشته‌های قبلی در مورد ویژگی‌های مختلف C# ۹.۰ یاد گرفتیم:دستورات سطح-بالاویژگی‌های Init-onlyدر این نوشته، بیایید به یکی دیگر از ویژگی‌های بسیار جالب C# ۹.۰ که انواع record یا records نامیده می‌شود نگاه کنیم.​کار کردن با داده‌های تغییر ناپذیر بسیار قدرتمند است، اغلب منجر به باگ‌های کمتری می‌شود، و شما را مجبور می‌کند تا اشیا را تبدیل به اشیای جدید کنید به جای اینکه اشیای موجود را اصلاح کنید. توسعه دهندگان #F به این عادت کرده اند، زیرا #F همه چیز را به طور پیش‌فرض به عنوان تغییرناپذیر در نظر می‌گیرد. حالا شما انواع تغییرناپذیر را در C# ۹.۰ نیز دارید، یا به اصطلاح انواع record، یا فقط records نامیده می‌شود. Records کار با داده‌های تغییرناپذیر در #C را برای شما آسان‌تر می‌کند. قبل از این که نگاهی به records در این نوشته بیاندازیم...بیایید با یک کلاس شروع کنیمدر نوشته قبلی شما در مورد ویژگی‌های init-only در C# ۹ یاد گرفتید. من کلاس Firend را با دو خصوصیت FirstName و LastName با ویژگی init-only ایجاد کردم. اگر نمی‌دانید ویژگی init-only چیست، لطفا نوشته قبلی را بخوانید:با ویژگی‌های init-only، شما خصوصیت‌های تغییرناپذیر را به دست می‌آورید. در کلاس Firend در تکه کد بالا، تمام ویژگی‌های آن init-only و بنابراین تغییرناپذیر هستند، شما نمی‌توانید آن‌ها را تغییر دهید. این بدان معناست که هنگام کار با این کلاس، شما مقدار خصوصیت را نمی‌توانید تغییر دهید. ​اگر لازم است چیزی را تغییر دهید، با داده‌های به روز شده یک شی جدید Friend ایجاد می‌کنید. این کاری است که شما هنگام کار با داده‌های تغییر ناپذیر انجام می‌دهید. به جای تغییر یک شی در طول زمان، شما یک شی جدید را در زمان نیاز به تغییر ایجاد می‌کنید. این به این معنی است که شی شما نشان‌دهنده وضعیت داده‌ها در یک نقطه زمانی خاص است.اجازه دهید مثالی از نحوه انجام این کار برایتان بزنم. بیایید یک شی Friend مانند زیر ایجاد کنیم:حالا بیایید فرض کنیم که در نقطه‌ای از برنامه شما نیاز دارید که lastname شی friend را به Mueller تغییر دهید، که یک نام بسیار رایج در آلمان است (‏شما ممکن است گلزنان مشهور فوتبال، Gerd Mueller و Thomas Mueller را بشناسید)‏. همانطور که با داده‌های تغییرناپذیر کار می‌کنید، نمی‌توانید خصوصیت LastName را تغییر دهید. به جای انجام این کار، شما یک شی Friend جدید ایجاد می‌کنید که نشان‌دهنده وضعیت جدید است. شما ممکن است آن شی Friend جدید را مانند تکه کد زیر بسازید. توجه داشته باشید که من چگونه مقدار خصوصیت FirstName را از شی Firend اول به مقدار خصوصیت FirstName شی Friend دوم نسبت می‌دهم:اما زمانی که شما ویژگی‌های بیشتری داشته باشید این رویکرد آزاردهنده است. بیایید یک خصوصیت Middlename را به کلاس Friend اضافه کنیم:حالا شما در تکه کد زیر می‌بینید که ایجاد شی Firend دوم با lastname جدید نیز یک خط کد اضافی برای آن خصوصیت Middlename جدید در پی دارد. به این دلیل است که شما باید این خصوصیت را از شی Firend قدیمی نیز کپی کنید:این بدان معناست که هرچه خصوصیات شما بیشتر باشد، این مسئله سخت‌تر می‌شود. البته شما می‌توانید برخی از منطق کپی با reflection یا serialization پیاده سازی کنید، همچنین می‌توانید از کتابخانه auto-mapping نیز استفاده کنید. اما C# ۹.۰ راه بهتری برای کار با کلاس‌های داده تغییرناپذیر دارد: Records.اولین Record خود را ایجاد کنیدبرای تغییر کلاس Friend به یک record، شما از کلمه‌کلیدی record به جای کلمه‌کلیدی class استفاده می‌کنید. در زیر شما نوع متناظر را به عنوان یک نوع record می‌بینید:ایجاد کپی‌هایی با عبارت Withدر تکه کد زیر شما روشی را می‌بینید که ما در این نوشته برای ایجاد یک شی Firend جدید با lastname جدید استفاده کرده‌ایم. این رویکرد همچنین با نوع record نیز کار می‌کند، اما واقعا کارآمد نیست: شما باید تمام مقادیر خصوصیت‌ها را به صورت دستی از شی اصلی کپی کنید، و اگر آن را به صورت دستی همانند تکه کد زیر بنویسید ممکن است یک خصوصیت را در کد خود فراموش کنید.عبارت with به شما این امکان را می‌دهد که یک شی جدید را با کارایی بیشتری ایجاد کنید. شما آن را در عمل در تکه کد زیر می‌بینید، و آن کد منجر به همان نتیجه‌ای می‌شود که در تکه کد بالا می‌بینید. آخرین دستور در تکه کد زیر از عبارت with برای ایجاد یک شی Friend جدید از شی Friend موجود ذخیره شده در متغیر friend استفاده می‌کند. شما می‌توانید این دستور را اینطور بخوانید: از مقدار خصوصیت‌های شی Friend موجود ذخیره شده در متغیر friend برای ایجاد شی Friend جدید، و تنظیم خصوصیت LastName شی Friend جدید به Mueller استفاده کنید. شی Friend جدیدی که توسط عبارت with با مقدار خصوصیت‌های نشان‌داده‌شده در commentها، متغیر newFriend تولید می‌شود، ذخیره کنید.​همانطور که در تکه کد بالا می‌بینید، عبارت with از یک ترکیب جالب در آغازگر شی برای ایجاد مقادیر جدید خصوصیت‌های مشخص استفاده کرده است. به این معنی است که اگر با آغازگر شی‌ها آشنا باشید، به سرعت با این ترکیب جدید سرعت خواهید گرفت. اما به یاد داشته باشید، عبارت with تنها با انواع record کار می‌کند و نه با کلاس‌های معمولی. ​زمانی که با داده‌های تغییر ناپذیر کار می‌کنید، یک کپی از شی خود را برای یک تغییر ایجاد می‌کنید. این روش به عنوان تغییر غیر-مخرب شناخته می‌شود. به جای داشتن یک شی منفرد که نشان‌دهنده وضعیت در طول زمان است، شما اشیا تغییر‌ناپذیری دارید که هر کدام نشان‌دهنده وضعیت در یک زمان مشخص هستند.بررسی کنید که Recordهایتان برابر هستند یا خیرانواع recordها از reference type هستند و value type مانند structها نیستند. اما متد Equals آن‌ها به طوری اجرا می‌شود که تمام مقادیر خصوصیت‌ها را برای برابری مقایسه می‌کند. در واقع کامپایلر #C، متد  Equals را برای شما ایجاد می‌کند.​ و کامپایلر همچنین overloadهایی برای اپراتورهای == و =! را ایجاد می‌کنید، بنابراین این اپراتورها از متد Equal استفاده می‌کنند. این ویژگی دیگری از انواع record است. این بدان معنی است که شما می‌توانید دو record را با ارزش خصوصیت‌های آن‌ها برای برابری مقایسه کنید. کد در زیر این را در عمل نشان می‌دهد. ابتدا یک شی Friend ایجاد شده و در متغیر friend ذخیره می‌شود. سپس عبارت with برای ایجاد شی Friend دیگر از شی firend موجود استفاده می‌کند. مقدار خصوصیت LastName شی Friend جدید، Mueller قرار داده شده ‌است. سپس دو شی Friend با  عملگر == مقایسه می‌شوند. نتیجه false است چون خصوصیت LastName شی جدید Huber ،Friend نیست و مقدار آن Mueller می‌باشد.بعد از دستور Console.WriteLine شی Friendسوم از شی newFriend ایجاد می‌شود. از عبارت with برای تنظیم مقدار خصوصیت LastName به Huber استفاده می‌شود؛ و شی Friend ایجاد شده در متغیر anotherFriend ذخیره می‌شود. این بدان معنی است که شی ذخیره‌شده در متغیر anotherFriend مقادیر خصوصیت‌ها، مشابه با اولین شی Friend‌ای است که در متغیر friend ذخیره شده است. سپس آن شی Friendاول با شی Friendسوم ذخیره‌شده در متغیر anotherFriend با  عملگر == مقایسه می‌شود. نتیجه دوباره در کنسول با دستور  Console.WriteLine نوشته می‌شود. در این حالت، نتیجه true است، زیرا خصوصیت‌های دو شی Friend شامل مقادیر یکسانی هستند.بررسی برابری یکی دیگر از ویژگی‌های قدرتمند انواع record است. با فراخوانی متد Equals یا با استفاده از عملگر == تمامی مقادیر خصوصیت‌ها را مقایسه می‌کنیم. در واقع، نوع IEquality&lt;T&gt; ،record را پیاده‌سازی می‌کند، در مورد نوع IEquality&lt;Friend&gt; ،Friend را پیاده‌سازی می‌کند.کامپایلر چه چیزی تولید می‌کند؟زمانی که فایل dll. را در برنامه Intermediate Language Disassembler باز کنید (ILDASM.exe - آموزش)، می‌توانید نوع Friend را بررسی کنید تا همه چیزهایی را که کامپایلر #C تولید کرده ببینید. در تصویر زیر می‌توانید نوع رکورد Friend را در Intermediate Language Disassembler ببینید. در واقع کامپایلر #C یک کلاس برای نوع رکورد Friend با خصوصیت‌های LastName ،FirstName و MiddleName ایجاد می‌کند. آنچه که در اینجا مشاهده می‌کنید پیاده‌سازی &lt;IEquatable&lt;Friend می‌باشد. چیز‌های دیگری هم می‌توانید ببینید، برای مثال یک کپی از سازنده که یک شی Friend را دریافت می‌کند، در تصویر زیر در بخش ctor : void(class Friend). نمایش داده شده است.زمانی که بر روی کپی سازنده دو بار کلیک می‌کنید، می‌توانید کد زبان میانی (Intermediate Language Code) را ببینید. کلمه family در خط اول بیانگر این است که سازنده Protected است. سازنده تمام مقدارهای انتقال داده شده به شی Friend را در خصوصیات LastName ،FirstName و MiddleName شی جدید Friend کپی می‌کند.بنابراین، شما می‌توانید فکر کنید که کپی سازنده چیزی شبیه به این در #C است:هنگامی که از عبارت with همانند تکه کد زیر استفاده می‌کنید، کپی سازنده برای ایجاد یک کپی جدید متناظر با شی Friend که با عبارت with مشخص می‌کنید، فراخوانی می‌شود. پس از آن خصوصیت LastName با توجه به آغاز کننده شی که با عبارت with مشخص کردید، مقداردهی می‌شود.در کنار کپی سازنده، کد‌های بیشتری نیز برای انواع record ایجاد می‌شود. آن کد ایجاد شده توسط کامپایلر قسمتی است که تمام جادوها اتفاق می‌افتد. یکی دیگر از ویژگی های مفید تولید شده توسط کامپایلر، متد محافظت شده PrintMembers است. یک StringBuilder را به عنوان یک پارامتر در نظر می‌گیرد و نام و مقدار تمام خصوصیت‌ها را به شی StringBuilder اضافه می‌کند. متد PrintMembers با استفاده از متد بازنویسی شده ToString فراخوانی می‌شود که این هم توسط کامپایلر ایجاد می‌شود. تکه کد زیر، خصوصیت‌های متعلق به شی Friend را در کنسول چاپ می‌کند، Console.WriteLine متد ToString را روی شی Friend فراخوانی می‌کند.خروجی کنسول مانند زیر است. همانطور که مشاهده می کنید، ابتدا نوع چاپ شده و سپس همه خصوصیت‌ها:خلاصهدر این نوشته انواع record که با C# 9.0 معرفی شدند را یاد گرفتید. آن‌ها کار با شی داده‌های تغییرناپذیر در #C را به یک لذت تبدیل می‌کنند. برای توسعه دهندگان #F این چیز جدیدی نیست، اما برای توسعه دهندگان #C پیشرفت بزرگی در این زبان است. with یک دستور قوی و زیبا است که تنها برای records در دسترس است، همچنین records موقعیتی عالی برای تولید خصوصیت‌ها و سازندگان/پایان‌دهندگان هستند.Source: Thomas Claudius Huber - C# 9.0: Records – Work With Immutable Data Classes✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>سپهر اسلامی</author>
                <pubDate>Sun, 22 Aug 2021 18:09:32 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش Unit Testing با استفاده از NUnit و Moq بخش دوم: Mocking</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-unit-testing-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-nunit-%D9%88-moq-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-mocking-gwtqet1h9omn</link>
                <description>در این مقاله قصد داریم که به Mocking و کتابخانه محبوب Moq بپردازیم. https://vrgl.ir/rZMij در این مقاله از GitHub Gist استفاده شده است و لود شدن قسمت مربوط به کد ها ممکن است کمی زمانبر باشدتعریف Mock معمولا Unit Test ها روی یک Unit کار میکنند که به یک کلاس اطلاق می شود. گاهی اوقات این Unit یک سری Dependency هایی را همراه خود دارند که جزئی از تست نیستند ولی Unit مربوطه بدون این dependency ها نمی تواند کار کند.سناریویی را در نظر بگیرید که ClassA سیستم تحت تست می باشد ولی این کلاس در داخل خود از ClassB استفاده می کند. حال ما نیاز داریم که ClassB را در تست خود جایگزین کنیم و تنها ClassA را تست کنیم. در اینجا باید از Test Double ها استفاده کنیم. یکی از Test Double ها Mock می باشد. Mock در واقع یک Fake Object می باشد که میتواند مقدار تعریف شده را به عنوان Result خود برگرداند.همچنین یک Mock می تواند شامل اطلاعات اضافی مانند اینکه چه پارامتر هایی مورد استفاده قرار گرفته و چند بار یک Mock Object صدا زده شده را برگرداند.کتابخانه Moqیک کتابخانه بسیار کاربردی در زمینه Mocking می باشد که قابلیت های بسیاری از جمله شبیه سازی متد ها،Event ها، پراپرتی ها و... را در اختیارمان قرار میدهد.نصب Moqبه سراغ پروژه ای که در قسمت قبلی ایجاد کرده ایم می رویم. در Package Manager Console دستور زیر را برای نصب Moq اجرا میکنیم.Install-Package Moq -Version 4.16.1همچنین می توانیم از قسمت Manage Nuget Packages به دنبال Moq بگردیم و آن را نصب کنیم.شروع کار با Moqاینترفیس ILog زیر را در بگیرید. https://gist.github.com/babaktaremi/78fc50fa890eab51ca3a4030340de3c6 این اینترفیس یک متد ساده با نام WriteMessage دارد. حال کلاس BankAccount را در نظر بگیرید که به این اینترفیس وابستگی دارد. https://gist.github.com/babaktaremi/d48146695c884daa55521dfffbee4509 هدف ما تست یونیت BankAccount می باشد نه اینترفیس ILog. در اینجا کتابخانه Moq به کمک ما می آید. https://gist.github.com/babaktaremi/28cbe1a8d9040046f2548fc1a0a397c8 در اینجا در متد SetUp یک Mock Object از اینترفیس ILog ساخته ایم و آن را به BankAccount پاس داده ایم. بوسیله آن نیازمندی یونیت BankAccount به ILog تامین می شود و می توانیم یونیت BankAccount را بدون نیاز به ساخت اینترفیس ILog تست کنیم. در ادامه با سایر قابلیت های Moq آشنا می شویم.ایجاد Mocking Methodsاینترفیس IFoo زیر را در بگیرید. در ادامه مقاله قصد داریم که بدون نیاز به پیاده سازی این اینترفیس، آن را Mock کنیم. https://gist.github.com/babaktaremi/8ad3ff7140a15b0503703562bd6dbd71 ابتدا یک mock از اینترفیس IFoo می سازیم. https://gist.github.com/babaktaremi/b8a117e7070a130caa07e43f663f8154 کلاس mock یک متد با نام Setup در اختیارمان قرار میدهد که به وسیله آن میتوانیم رفتار mock را تغییر دهیم. رفتار متد DoSomething را به شکل زیر تعریف میکنیم https://gist.github.com/babaktaremi/7f90c024856b39df2441cd5b5e0d0497 خط 1 نشان میدهد که اگر متد هرکدام از مقدار Ping یا Foo را دریافت کرد False را به عنوان نتیجه بازگرداند و در خط 2 مقدار Pong توسط متد دریافت شد، مقدار True را بازگرداند. حال اگر متد  DoSomething مقداری غیر از Ping، Pong و یا Foo را دریافت کند، نتیجه آن مقدار Default خروجی متد می باشد که در این حالت مقدار Default برای boolean مقدار False می باشد.بررسی Mocking برای متدهای وابسته به مقدار ورودیمتد Add از اینترفیس IFoo را در نظر بگیرید. فرض کنید که میخواهیم وقتی که عدد زوج است، مقدار True را برگردانیم و یا وقتی که ورودی در رنج اعداد 1 تا 10 بود مقدار False را برگردانیم. ویا برای متد DoSomething این شرط را داشته باشیم که ورودی String باید شامل حروف باشد.کلاس It از Moq شامل چند متد است که بوسیله آن  میتوانیم برای خروجی متد ها شرط بگذاریم. https://gist.github.com/babaktaremi/f1f1580d4a17a8694556f1bbb1d092e4 بررسی Mocking برای پارامترهای Ref و Outبرای Mock کردن پارامتر out در Moq می توانیم به شکل زیر عمل کنیم https://gist.github.com/babaktaremi/3f82e1adad045defcc16200d14fb2d49 دقت کنید که در متد بالا تعریف کرده ایم که اگر ورودی TryParse مقدار Ping بود،خروجی متد مقدار True داشته باشد و out Argument را به مقدار ok تطبیق دهد.برای Ref میتوانیم مانند زیر عمل کنیم.دقت داشته باشید که در مورد Ref کتابخانه Moq به صورت Reference Equality عمل میکند و Reference دو آبجکت را بررسی میکند. https://gist.github.com/babaktaremi/0b3acd4a18cdcfad45765616ecb8df4d بررسی مقدار بازگشتی برای Mocking متدهاهنگامی که مقدار بازگشتی متد باید دارای مقدار خاصی باشد میتوانیم از یک Lambda Expression برای خروجی مقدار Returns استفاده کنیم. به طور مثال مقدار ProcessString در Mock زیر همواره کاراکتر ها را lower کرده و برمیگرداند. https://gist.github.com/babaktaremi/1c596e7998d3cf17526575a4ad4155e0 بررسی CallBack در Moqموقع Setup کردن یک متد میتوانیم تعریف کنیم که به ازای هر Call مقدار های مختلف داشته باشیم. مثلا میتوانیم تعریف کنیم که هربار متد Call شد یک Counter مقدارش زیاد شود که میتوانیم از CallBack به شکل زیر استفاده کنیم. در واقع CallBack  یک فانکشن است که هربار که متد فراخوانی میشود،مقدار تعریف شده در  CallBack اجرا می شود. https://gist.github.com/babaktaremi/b3944e368cc592a7dfd5cb9655662d13 بررسی Exception ها در Moqبرای اینکه یک Exception را در Moq شبیه سازی کنیم میتوانیم به صورت زیر عمل کنیم. https://gist.github.com/babaktaremi/b1a2af54ce727cbbd232378034284798 بررسی Property Mockingدر mock Object نمیتوان که مقداری را به یک Property تخصیص داد چرا که در اصل هیج Object ای وجود ندارد. میتوانیم به صورت زیر یک مقدار را به یک پراپرتی Assign کنیم. https://gist.github.com/babaktaremi/bb50ba21886bdc31a4613ab472d05af7 در مورد Setter های یک پراپرتی می توانیم مانند زیر عمل کنیم و هربار که Setter یک پراپرتی فراخوانی شد، عملیاتی را انجام دهیم. https://gist.github.com/babaktaremi/4cfe85e79d84e750a47fd071e72db145 بررسی Value Tracking پراپرتی هامیتوانیم به شکل زیر، تمامی پراپرتی های موجود در mock را طوری تنظیم کنیم که رفتاری مانند یک پراپرتی داشته باشند. https://gist.github.com/babaktaremi/80c636a800f2f88f8b5c33d33ef873b0 در خط 5 تمامی پراپرتی های ابجکت mock را تنظیم میکنیم که رفتاری مانند پراپرتی داشته باشند.بررسی Event Mockingفرض کنید که اینترفیس زیر را به عنوان نمونه داریم که دارای یک Event و یک Delegate می باشد. https://gist.github.com/babaktaremi/631674a96e43b84a752ec769b0191f26  https://gist.github.com/babaktaremi/c584293b5b681ba58b9e687f3728d6a0 حال یک کلاس با نام Doctor به شکل زیر ایجاد میکنیم که وابستگی به اینترفیس IAnimal دارد و به Event ها Subscribe کرده است. https://gist.github.com/babaktaremi/003860bdfed6be1a6783f4562ae997bf برای Raise کردن یک Event می توانیم مانند زیر عمل کنیم. https://gist.github.com/babaktaremi/deafd22ffda5388fbdcf2b637768c7db میتوانیم در SetUp یک متد، Raise کردن یک Event را در نظر بگیریم. https://gist.github.com/babaktaremi/36591474d085d8ef25a787734ded5918 میتوانیم یک Custom Event داشته باشیم. به طور مثال در اینترفیس IAnimal یک delegate داریم که میتوانیم از آن به شکل زیر در mock object استفاده کنیم. https://gist.github.com/babaktaremi/a2593a33ea878ff133eb6cc97b918c00 بررسی دسترسی به Protected Members در Mockفرض کنید که کلاس Person را به شکل زیر داریم که دارای اعضای Protected می باشد. https://gist.github.com/babaktaremi/8f6adf19e6344accd7c4712c942cc437 میتوانیم به شکل زیر به اعضای Protected دسترسی داشته باشیم و همچنین متدهای Protected را Invoke کنیم. https://gist.github.com/babaktaremi/f096765cd7a2681ac7bacb5e0599ea5e دقت داشته باشید که که برای Invoke کردن متد به جای استفاده از دستور It باید از دستور ItExpr استفاده کنیم.نتیجه گیریدر این مقاله با Mocking و کتابخانه Moq آشنا شدیم. به طور کلی کتابخانه Moq ابزاری فوق العاده قدرتمند برای شبیه سازی بخش هایی از یونیت که مورد نظر تست نیستند می باشد. اگر به کد های این بخش نیاز داشتید میتوانید آن را از گیت هاب به لینک زیر دریافت کنید. خوشحال میشوم که نظرات شما را راجب این مقاله بدانم و همچنین اگر دوست داشتید میتوانید من را به یک قهوه مهمان کنید. https://coffeebede.ir/buycoffee/bobby  https://github.com/babaktaremi/NUnit-Drafts مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Mon, 14 Jun 2021 13:39:59 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش Unit Testing با استفاده از NUnit و Moq بخش اول: آشنایی با NUnit</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-unit-testing-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-nunit-%D9%88-moq-%D8%A8%D8%AE%D8%B4-%D8%A7%D9%88%D9%84-%D8%A2%D8%B4%D9%86%D8%A7%DB%8C%DB%8C-%D8%A8%D8%A7-nunit-qfl0xvoueyfq</link>
                <description>در این سری آموزش قصد داریم به فریم ورک محبوب NUnit و همچنین Mocking بوسیله کتابخانه محبوب Moq بپردازیم.در این مقاله از GitHub Gist استفاده شده است و لود شدن بخش مربوط به کد ها ممکن است کمی زمانبر باشد. همچنین ممکن است نیازمند نرم افزار رفع تحریم باشید.تعریف Unit Testبه عمل نوشتن کدی که بررسی درستی نتیجه سیستم یا بخشی از آن را برعهده دارد Test گفته میشود. یک Unit به کوچک ترین بخش کد که تست پذیر باشد گفته می شود.معمولا Unit به یک کلاس اطلاق میشود که متد ها و رفتار های آن قابل تست باشد. تست واحد یا Unit Test بررسی میکند که یک عملیات نتیجه پیش بینی شده را بر میگرداند یا خیر.مفهوم Red-Green-Refactorاین مفهوم در Test Driven Development برای اولین بار بیان شد. در تکنیک Red Green Refactor رویه زیر را پیشنهاد میکندتست بنویسیدتست را اجرا کنید و ببینید که تست نتیجه اشتباه را بر میگرداند و fail میشودکد اصلی را بنویسیدتست را دوباره اجرا کنیددر صورتی که تست pass شد(نتیجه آن سبز شد) کد را refactor کنیدآشنایی با NUnitیک فریم ورک open source مخصوص Unit Testing در دات نت می باشد که کار Unit Testing را بسیار آسان می کند. به وسیله NUnit میتوانیم یک پروژه مخصوص Unit Testing داشته باشیم و تست ها را به راحتی مانیتور کنیم. در تاریخ نوشتن این مقاله طبق آمار NugetTrends.com پکیج مربوط به NUnit بیش 117 میلیون بار دانلود و نصب شده است.آمار دانلود پکیج NUnitایجاد پروژه نمونهپس از کلیک بر روی Create New Project به سراغ ساخت یک Class Library می رویم و سپس یک نام برای آن انتخاب میکنیم.سپس به سراغ نصب پکیج NUnit می رویم. میتوانیم از قسمت Package Manager Console و با دستور زیر پکیج NUnit را نصب کنیم.Install-Package NUnit -Version 3.13.2 و یا میتوانیم بر از طریق Manage Nuget Package به سراغ پکیج NUnit رفته و آن را نصب کنیمپس از نصب NUnit آیکون مربوط به Class Library تغییر میکند که نشان میدهد که این پروژه به تست تغییر کاربری داده است.ایجاد اولین تستابتدا یک کلاس ایجاد میکنیم و آن را با صفت TextFixture علامت گذاری میکنیم. سپس یک متد ایجاد کرده و آن را با صفت Test علامت گذاری میکنیم. در اینجا System Under Test و یا به صورت اختصار SUT را یک عملیات ساده در نظر میگیریم https://gist.github.com/babaktaremi/d28c019d04af7ea0fdebed0a508cabf9 از قسمت search  به دنبال Test Explorer می گردیم.محیط Test Explorer به شکل زیر باز میشود.بر روی گزینه Run کلیک میکنیم و مشاهده میکنیم که تست ها Pass می شوند.آشنایی با ReSharperیک ابزار بسیار قدرتمند که به نوشتن کد بهتر کمک بسیاری میکند و برای هر کد Suggestion های بسیار خوبی را ارائه میدهد. ReSharper در زمینه Unit Testing نیز Test Session بسیار قدرتمندی را ارائه میدهد که  تست کد را بسیار آسان میکند. برای دانلود ReSharper و نصب آن میتوانید به این لینک مراجعه کنید.پس از نصب ReSharper یک گزینه کنار هر تست به شکل زیر نمایان می شود که بوسیله آن میتوان تست را به راحتی Run کرد.آشنایی با Assertion در NUnitیک  Unit Test معمولا دو نتیجه را در بر دارد . یا تست Fail و Red میشود و یا تست Pass و Green میشود. به وسیله Assertion می توان نتیجه یک تست را طبق شرایط بدست آورد. در مثال قبل به وسیله Assertion نتیجه Unit Test را بدست آوردیم که 2+2=4 و تست pass شد.آشنایی با Assertion های سادهنتیجه Inconclusive: وقتی یک تست Inconclusive باشد نتیجه آن مشخص نیست و در نتیجه نمیتوان مشخص کرد که تست Pass شده است و یا Fail شده است. بوسیله دستور زیر میتوان نتیجه تست را Inconclusive کرد. https://gist.github.com/babaktaremi/cc7c293573fc02aed64c6aed6ff60a7b مشاهده میکنیم که نتیجه تست با رنگ زرد Inconclusive نشان داده شده است.ایجاد warning: وقتی یک warning ایجاد میکنیم نتیجه تست به صورت خودکار Inconclusive میشود. به صورت زیر میتوانیم در یک تست ایجاد warning کنیم و یک message را نمایش دهیم. https://gist.github.com/babaktaremi/6f358398e272b35574f8ba5bf1c0e642 ایجاد Equality Check: وقتی میخواهیم نتیجه دو مقدار را از نظر برابری چک کنیم میتوانیم از دستورات زیر استفاده کنیم. https://gist.github.com/babaktaremi/d884796a5fccba05c0bc3c9fb1808735  https://gist.github.com/babaktaremi/78ce0e9ca17fb287954e1b8f14aaa83a نکته قابل توجه این است که دستور Assert.That و Is میتواند دستورات مختلفی را چک کند مانند:دستور Is.False: این دستور چک میکند که نتیجه یک Assert برابر False باشد.دستور Is.Ordered : بررسی میکند که یک Collection به صورت مرتب شده باشد.دستور Is.Unique : بررسی میکند که تمام المان های یک Collection یکتا و Unique باشد.دستور Is.AssignableFrom: بررسی میکند که یک type از نوع type تعریف شده باشدمابقی قسمت های مربوط به Assert.That را میتوانید در تیکه کد زیر ببینید. https://gist.github.com/babaktaremi/df61f960eeb6d8629c8685e708da41e5 بررسی String Assertionعلاوه بر Assert معمولی، میتوانیم بوسیله String Assertion بررسی های مخصوص به String را انجام دهیم. کارهایی که میتوانیم با string Assertion انجام دهیم که پرکاربرد ترین آنها عبارتند از:دستور AreEqualIgnoringCase:  بررسی میکند که دو string باهم برابرند بدون در نظر گرفتن بزرگ و کوچک بودن کاراکتر های آنها.دستور Contains: بررسی میکند که یک string شامل یک sub string خاص هست یا خیر.دستور IsMatch : بررسی میکند که آیا یک string با یک regular expression برابری میکند یا خیر.لیست کامل دستورهای string assertion را میتوانید در قطعه کد زیر ببینید. https://gist.github.com/babaktaremi/3a6e70775f248b49d446dbcbaa2c8c4b بررسی Collection Assertionعلاوه بر string Assertion یک سری Assertion خاص برای Collection ها وجود دارد که میتوانیم بوسیله آن بررسی های مخصوص به Collection ها را انجام دهیم که پرکاربرد ترین آنها عبارتند از:دستور AreEqual :  بررسی میکند که آیا دو کالکشن دارای المان هایی با تعداد یکسان و ترتیب یکسان هستند.دستور AreEquivalent: بررسی میکند که آیا دو کالکشن المان هایی یکسان دارند یا خیر.لیست کامل دستورات Collection Assertion را میتوانید در قطعه کد زیر ببینید. https://gist.github.com/babaktaremi/2a3d5abc4d76669007d6cb3fefd16e7b بررسی صفت های پرکاربرد در NUnitدر NUnit میتوانیم متد ها و کلاس ها را بوسیله صفت ها علامت گذاری کنیم. برخی از این صفت ها عبارتند از:صفت Test : یک Test Method را بویسیله این صفت علامت گذاری میکنیم. در واقع هر متدی که نشان دهنده یک تست باشد باید این صفت را داشته باشد.صفت TestFixture :  بوسیله این صفت یک کلاس که شامل چند تست می باشد را علامت گذاری میکنیم.صفت SetUp : وقتی متدی با این صفت علامت گذاری شود قبل از هر تست اجرا میشود.صفت OneTimeSetUp : وقتی متدی با این صفت علامت گذاری شود، یکبار و فقط یکبار قبل اجرای تمامی تست ها اجرا میشود.صفت OneTimeTearDown : وقتی متدی با این صفت علامت گذاری شود ، پس از اجرای تمامی Test Case ها یکبار اجرا میشود.صفت TearDown : وقتی متدی با این صفت علامت گذاری شود، پس از اتمام هر Test Case اجرا می شود.نمونه ای از استفاده از این صفت ها را میتوانید در قطعه کد زیر ببینید. https://gist.github.com/babaktaremi/396470254e4af87276e8193be7deca04 بررسی  Data Driven Tests در NUnitبرای بررسی یک تست با data خاص دو روش وجود دارد.استفاده از صفت TestCaseدر NUnit و با استفاده از صفت TestCase میتوانیم یک Test Method را با داده های اختیاری تست کنیم. سناریو ساده زیر را در نظر بگیرید. https://gist.github.com/babaktaremi/ad7201490a25f24222bfcf41f942d57b حال این کلاس Price Calculator را میتوانیم بوسیله Test Case با مقدار های مختلف به شکل زیر تست کنیم: https://gist.github.com/babaktaremi/e56ac9527be9608f531dcd785bc26580 همچنین میتوانیم با استفاده از پراپرتی ExpectedResult میتوانیم خروجی متد را به شکل دلخواه در بیاوریم و آن را با Expected Result مقایسه کنیم. https://gist.github.com/babaktaremi/c7b5e5ce47fcab4aaea4e2c4543c29e0 استفاده از صفت TestCaseSourceوقتی میخواهیم یک تست را بر مبنای دیتای خارجی تست کنیم میتوانیم از TestCaseSource استفاده کنیم. این صفت نام یک پراپرتی Static که یک لیستی از TestCaseData بر میگرداند را میگیرد. فرض کنید که یک فایل csv به شکل زیر داریم و میخواهیم دیتای آن را برای یک Test Case وارد کنیم. https://gist.github.com/babaktaremi/7c069c7e08e4f6effdbf71d5611c8ee3 یک پراپرتی Static با نام TestCases مینویسیم که دیتا را از داخل این فایل csv بخواند و آن را برگرداند https://gist.github.com/babaktaremi/7699f364b666537fcae13ddc0c472726 حال این پراپرتی را در داخل TestCaseSource به شکل زیر استفاده میکنیم. https://gist.github.com/babaktaremi/ee88ed9d9532e888e34d1a0249d36774 نتیجه گیریدر این مقاله با NUnit این فریم ورک قدرتمند و محبوب در زمینه Unit Testing آشنا شدیم . به طور کلی نوشتن تست به داشتن کدی قابل اطمینان کمک شایانی میکند. در قسمت بعدی با  Mocking و کتابخانه Moq آشنا میشویم. خوشحال میشوم که نظرات شما را درباره این مقاله بدانم و همچنین اگر دوست داشتید میتوانید من را به یک قهوه مهمان کنید. https://coffeebede.ir/buycoffee/bobby مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Thu, 20 May 2021 11:53:40 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش زمانبندی کارها با HangFire در Asp.Net Core</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-hangfire-%D8%AF%D8%B1-aspnet-core-gn4yeejcbafx</link>
                <description>در این مقاله برای نمایش کد ها از GitHub Gist استفاده شده و ممکن است Load شدن کد ها کمی طول بکشد یا به نرم افزار های رفع تحریم نیاز داشته باشید.تسک های پس زمینه (Background Job) چیست؟بطور کلی تسک های پس زمینه کارهایی هستند که برنامه باید بصورت خودکار در زمان های مشخص آن هارا انجام دهد برای مثال :شرایطی را در نظر بگیرید که متدی با حجم زیادی از محاسبات پیچیده دارید که وقتی کاربر درخواست خود را ارسال میکند شروع به محاسبه میشود و کاربر جاری چاره ای جز انتظار نخواهد داشت اما اگر اینکار در زمانی دیگر قبل از درخواست کاربر محاسبه میشد و صرفا نتیجه به کاربر نمایش داده میشد قطعا تصمیم بهتری نسبت به محاسبه آنی آن متد در زمان درخواست کاربر بوده.در سناریو دیگر تصور کنید میخواهید هر شب در ساعتی مشخص خلاصه ای از مطالب وبسایتتان را برای کاربران وبسایت ایمیل کنید در این حالت برنامه باید هر شب در فلان ساعت اینکار را برای ما انجام دهد و تماما باید این اتفاق بدون دخالت هیچ اراده انسانی و بصورت خودکار توسط برنامه انجام گیرد.همچنین شرایطی از این قبیل ، ارسال ایمیل تایید هویت یک ساعت بعد از ثبت نام ، گرفتن بک آپ از اطلاعات برنامه بصورت هفتگی و دیگر این موارد همه در دسته تسک های پس زمینه(Background Job) از یک برنامه قرار دارند.سوال : HangFire چیست؟همانطور که دانستید تسک های پس زمینه نیاز به یک سیستم مدیریت زمان دارد که کارها را در زمان های مشخص شده به انجام برساند. HangFire یک پکیج متن باز برای ایجاد سیستم زمانبندی شده کارها است و اینکار را به ساده ترین روش انجام خواهد داد.همچنین HangFire در کنار Quartz که یک سیستم دیگر جهت پیاده سازی زمانبندی است از معروف ترین پکیج ها برای زمانبندی تسک های پس زمینه بشمار میرود که در ادامه بیشتر به مزایا و معایب این دو میپردازیم.مقایسه HangFire و Quartz : میتوان گفت این دو پکیج تا حد زیادی شبیه به هم هستند و تفاوت اصلی آن ها در لایه های زیرین و نوع محاسبات زمانی که هریک نهفته است که الگوریتم مختص به خود را برای این محاسبه دارند اما در نهایت یک کار را انجام میدهند.دیتابیس :تفاوتی که میتوان از آن نام برد وجود قابلیت Redis Store در HangFire است که Quratz چنین قابلیتی را از سمت خودش اراعه نداده و برای استفاده از Redis در Quartz باید شخصا این دو را باهم کانفیگ کنید. دیتابیس Redis بخاطر ساختار دیتابیسی که دارد سرعت و پرفرمنس بالاتری را اراعه میدهد که استفاده از این قابلیت در پروژه هایی با تعداد تسک ها و رکورد های زیاد کاملا مشهود است. البته این ویژگی در HangFire رایگان نیست و برای داشتن آن از سمت HangFire لازم است هزینه آن را نیز پرداخت کنید اما اگر هم نمیخواهید پولی بابتش بپردازید و همچنان از آن استفاده کنید یک پکیج اوپن سورس برای آن نیز طراحی شده که از لینک پایین میتوانید مشاهده کنید. https://github.com/marcoCasamento/Hangfire.Redis.StackExchange ساختار :پکیج HangFire از ابتدا با دات نت و معماری های دات نتی توسعه داده شده اما Quartz ابتدا برای زبان جاوا نوشته شده بود و به نوعی از این زبان ریلیزی برای دات نت تهیه شد و این موضوع طبعا تاثیرات خودش را داشته و برخی از معماری ها و تفکرات جاوایی در آن مشهود است که البته مشکلی ایجاد نمیکند و محدودیتی نسبت به HangFire از لحاظ کارکرد دارا نیست شاید تنها چیزی که میتوان در این باب گفت DotNet Friendly تر بودن HangFire است که کار با متد های آن آسان تر و به اصطلاح خوش دست تر است.داشبورد :هردو پکیج از داشبورد پشتیبانی میکنند که میتوانید در این داشبورد و ui اختصاصی که برای نمایش تسک ها طراحی شده تسک های ایجاد شده را مدیریت کنید. داشبورد HangFire بصورت پیشفرض همراه با آن قرار دارد که بعد از نصب HangFire میتوانید براحتی داشبورد سوار بر آن را نیز مشاهده کنید اما در Quartz ، داشبورد باید بصورت Extension در پکیجی جدا به آن اضافه شود و مورد استفاده قرار گیرد. در لینک پایین دوتا از بهترین داشبورد ها برای Quartz را مشاهده میکنید که در صورت نیاز میتوانید از آن استفاده کنید. https://github.com/jlucansky/Quartzmin  https://github.com/guryanovev/CrystalQuartz استفاده از HangFire :1. نصب :برای نصب HangFire در پروژه Asp.Net Core لازم است ابتدا پکیج های مورد نیاز آن را نصب کنید که شامل :Install-Package Hangfire.Core Install-Package Hangfire.SqlServerInstall-Package Hangfire.AspNetCoreپس از نصب پکیج ها باید تنظیمات مورد نیاز برای پیاده سازی HangFire در برنامه را اعمال کنیم. این تنظیمات شامل افزودن سرویس ها و اینترفیس های HangFire به برنامه است که اینکار را با افزودن HangFire به متد ConfigureService کلاس Startup انجام خواهیم داد : https://gist.github.com/sajadkardel/b2fe809e07e406eea12e6beeb8ba6d6e پکیج HangFire برای مدیریت کار و زمان ، Table هایی دارد که پس از نصب روی دیتابیس برنامه شما قرار میگیرد فقط باید دقت داشته باشید ConnectionString دیتابیس خود را در متد AddHangFire مقدار دهی کنید تا از این طریق دیتابیس برنامه را شناخته و Table های مورد نظر را در Schema جدیدی با نام HangFire به آن اضافه کند.پ ن : HangFire بصورت پیشفرض با دیتابیس SqlServer  ارتباط برقرار میکند.این پکیج یک داشبورد اختصاصی دارد که در آن لیستی از انواع تسک های در صف انجام و گزارشی از انجام شده ها را در اختیار ما قرار میدهد. برای تنظیم این داشبورد باید Middleware مربوط به آن و endpoint جدیدی برای شناسایی مسیر داشبورد HangFire در برنامه را در متد Configure کلاس Startup اضافه کنید : https://gist.github.com/sajadkardel/fb9088bf0aab1f506b297577a922c0f6 برای اینکه به داشبورد HangFire دسترسی داشته باشید کافیست پس از نصب و انجام تنظیمات مذکور ، برنامه را اجرا کنید و در انتهای Url برنامه ، کلمه &quot;hangfire&quot; را وارد کنید سپس وارد پنل داشبورد آن خواهید شد.http://localhost:50255/hangfireالبته میتوانید آدرس داشبورد HangFire در برنامه را از کلمه &quot;hangfire&quot; به هر چیزی که میخواهید شخصی سازی کنید. برای اینکار کافیست درون Middleware تعریف شده بصورت ورودی string آدرس جدیدی برای HangFire تعریف کنید.app.UseHangfireDashboard(&amp;quot/mydashboard&amp;quot);و به طبع در Url :http://localhost:50255/mydashboard2. داشبورد :داشبورد HangFire شامل چندین بخش و تب مختلف است که به اختصار هر یک را بررسی خواهیم کرد.تب Job :همه تسک های تعریف شده شامل Enqueued, Succeeded, Processing, Failed و... در این تب نشان داده میشود.تب Retries :این تب مربوط به تسک هایی است که در روال زمانبندی و اجرا به دلایل مختلف مثل Stop شدن برنامه توسط iis یا Down شدن سرور و یا هر عامل خارجی دیگر شکست خوردند و در زمانبندی مشخص شده اجرا نشدند. همچنین قابلیت دوباره به جریان انداختن job مورد نظر را در اختیار ما قرار میدهد که از این طریق میتوان تسک های از دست رفته را مدیریت کرد و دوباره انجام داد.تب Recurring Jobs :وقتی شما یک تسک مانند گرفتن بکاپ از دیتابیس بصورت ماهانه تعریف میکنید و قرار است در هر ماه این اتفاق رخ دهد این یک تسک تکراری تلقی شده و این تب مسئول نشان دادن اینگونه از تسک ها میباشد.تب Servers :این بخش سرویس هایی که HangFire برای محاسبه زمانبندی از آن استفاده میکند را نشان میدهد. وقتی متد services.AddHangfireServer را به متد ConfigureService کلاس Startup اضافه میکنید سرویس های HangFire جهت محاسبه زمانبندی ها فعال میشود.3. امنیت داشبورد :همانطور که دانستید داشبورد ، اطلاعات کاملی از نوع کار و زمان اجرای آن و نام متدها  را در اختیار ما قرار میدهد و همچنین اجازه تغییراتی مثل حذف یک تسک یا دوباره به اجرا در آوردن تسک ها و یا اجرای سریع تسک های به موعد نرسیده را به کاربر میدهد. گاهی ممکن است این اطلاعات شامل محتوایی امنیتی و غیر عمومی باشد که هرکسی در برنامه حق دسترسی به آنهارا ندارد. برای مدیریت کردن این امر میتوانید مراحل زیر را طی کنید :مرحله اول :  یک کلاس ایجاد میکنیم (مثلا با نام MyAuthorizationFilter) که این کلاس از اینترفیسی با نام IDashboardAuthorizationFilter ارث بری خواهد کرد. https://gist.github.com/sajadkardel/5d5f2d9470e9e04d2c8a8daa732021f9 درون این کلاس متدی با نام Authorize از اینترفیس مربوطه impliment میشود که شروط احراز هویت و صدور یا عدم صدور دسترسی را کنترل میکند. این متد یک خروجی Boolian دارد که اگر هر یک از شروط احراز هویت شما تایید نشد خروجی false برمیگرداند. در این مثال ما برای دسترسی محدودیت Login بودن را اعمال کرده ایم که این را از HttpContext میگیریم.مرحله دوم : در این مرحله کلاسی که بعنوان فیلتر احراز هویت برای کاربران ساخته ایم را در option های middleware پکیج HangFire اضافه میکنیم. https://gist.github.com/sajadkardel/06fb146cfda06d14a37b33d7fd323f9b یکی دیگر از option های این middleware که میتوان برای کنترل دسترسی در HangFire استفاده کرد آپشن Read-only view نام دارد. https://gist.github.com/sajadkardel/f73171e49cd2e6b69fbb3f4a2b31a52a  این آپشن اجازه هرگونه تغییر در روند تسک ها از طریق صفحه داشبورد را از هر کاربری سلب میکند و داشبورد را صرفا به جهت نمایش کار ها استفاده میکند نه چیز دیگر.انواع تسک ها در HangFire : 1. تسک های Fire-And-Forget :تسک های Fire-And-Forget زمانبندی خاصی ندارند و بلافاصله بعد از فراخوانی اجرا میشوند. برای مثال شرایطی را در نظر بگیرید که میخواهید پس از ثبت نام هر کاربر در وبسایت ، یک ایمیل خوش آمد گویی ارسال کنید. این عمل یک تسک پس زمینه تلقی میشود اما زمانبندی خاصی نیز نمیخواهید برایش در نظر بگیرید در چنین شرایطی میتوانید از متد Enqueue استفاده کنید و یک تسک Fire-And-Forget ایجاد کنید تا این تسک صرفا در تسک های پس زمینه تان نام برده شود و قابل مشاهده باشد. https://gist.github.com/sajadkardel/b3324e010d4ee653c96ff95848e72553 همانطور که میبینید در مثال بالا ابتدا برای استفاده از تسک های Fire-and-Forget در HangFire باید اینترفیس IBackgroundJobClient را تزریق کنیم و با استفاده از متد Enqueue در این اینترفیس یک تسک پس زمینه ایجاد میکنیم که کار آن فراخوانی متد SendWelcomeMail خواهد بود.2. تسک های Delayed : همانطور از اسم آن پیداست تسک های Delayed تسک هایی هستند که با یک تاخیر در زمان اجرا خواهند شد. بطور کلی زمانبندی این تسک ها به دو دسته تقسیم میشود : دسته اول : اجرا پس از تاخیر در زمان مشخص.همان شرایط ارسال ایمیل به کاربرانی که در وبسایتتان ثبت نام میکنند را در نظر بگیرید اما اینبار میخواهید نه بلافاصله بلکه 10 دقیقه بعد از ثبت نام کاربر ایمیل خوش آمد گویی را ارسال کنید. در این نوع شما یک تاخیر 10 دقیقه ای میخواهید که Delayed Job ها اینکار را برای ما انجام میدهند. https://gist.github.com/sajadkardel/52f55f6f9bc8e2629e3ba93e8514d262 در این مثال با استفاده از متد Schedule در اینترفیس IBackgroundJobClient توانستیم متد SendWelcomeMail را صدا بزنیم و با ورودی TimeSpan یک تاخیر 10 دقیقه ای در متد HangFire اعمال کنیم.همچنین میتوانید از ورودی های دیگر TimeSpan شامل  TimeSpan.FromMilliseconds و  TimeSpan.FromSecondsو TimeSpan.FromMinutes و TimeSpan.FromDays برای تنظیم تاخیر در تسک های خود استفاده کنید.دسته دوم : اجرا در زمان مشخص.نوع دیگر استفاده از متد Schedule تنظیم یک تاریخ و زمان مشخص برای اجرا شدن تسک های در آن تاریخ و زمان واحد میباشد. برای مثال سناریو ای را در نظر بگیرید که دستور اجرا و زمانبندی آن در اختیار کاربر باشد و کاربر بخواهد یک Reminder در تاریخ مشخص برایش ارسال شود که در اینصوررت میتوانید با استفاده از instance دیگری از متد Schedule که ورودی ای از جنس DateTimeOffset دریافت میکند ، تاریخ مشخص برای اجرا انتخاب کنید. https://gist.github.com/sajadkardel/496c958231b6d57d48e4337f34cb4bab در این مثال تاریخ مشخص برای اجرای تسک های خود را از کاربر در ورودی اکشن دریافت کرده ایم و به متد Schedule در غالب DateTimeOffset تعریف شده پاس میدهیم.3. تسک های Recurring :تسک های Recurring به تسک هایی گفته میشود که باید در یک بازه گردشی از زمان اجرا شوند. در یک مثال بیشتر با آن آشنا خواهیم شد. فرض کنید میخواهید هر هفته برنامه از اطلاعات دیتابیس موجود بکاپ بگیرد. در اینجا تسکی دارید که قرار است هر هفته و هربار به تکرر اجرا شود. https://gist.github.com/sajadkardel/58739a4ec12c713cf6cd8c9504a4beb2 برای تنظیم یک Recurring Job باید اینترفیس دیگری بنام IRecurringJobManager را تزریق کرده و متد AddOrUpdate را استفاده کنید. در ورودی این متد یک جنس تعریف شده در HangFire بنام Cron دریافت میشود که بازه گردش در زمان را دریافت میکند که در اینجا بصورت هفتگی است.انواع دیگر Cron شامل : هر دقیقه (Cron.Minutely) :این Cron هر دقیقه یکبار اجرا خواهد شد. _recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; job , Cron.Minutely);هر ساعت (Cron.Hourly) :این Cron هر یک ساعت یکبار و بصورت پیشفرض در دقیقه اول هر ساعت اجرا میشود._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Hourly);اما میتوانید یک ورودی دقیقه به آن بدهید که در اینصورت در N اُمین دقیقه از هر ساعت اجرا شود. _recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Hourly(10)); هر روز (Cron.Daily) :این Cron بصورت روزانه و در حالت پیشفرض در اولین ساعت و اولین دقیقه هر روز اجرا خواهد شد._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Daily);در حالتی دیگر میتوانید ورودی ساعت و دقیقه را به آن بدهید تا در ساعت و دقیقه ای مشخص در هر روز اجرا شود._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Daily(3,10)); هر هفته (Cron.Weekly) :این Cron هفتگی است. بصورت پیشفرض هر هفته شنبه در اولین ساعت و در اولین دقیقه اجرا میشود._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Weekly);در حالتی دیگر چندمین روز هفته و ساعت و دقیقه مشخص را در ورودی میگیرد و حول آن میچرخد._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job,Cron.Weekly(DayOfWeek.Monday,3,10));هر ماه (Cron.Monthly) :این Cron بصورت ماهانه اولین روز ماه در اولین ساعت روز و در اولین دقیقه ساعت زمانبندی خود را اعمال میکند._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Monthly);و در صورت دادن ورودی میتوانید زمانبندی آن در چندمین روز ماه در چه ساعت و دقیقه ای را نیز تنظیم کنید._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Monthly(10,3,10));هر سال (Cron.Yearly) :و در نهایت این Cron بصورت سالانه و در اولین ماه ، روز ، ساعت و دقیقه هر سال وظیفه خود را انجام خواهد داد._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Yearly);که اینهم مانند بقیه ، ورودی هایی دریافت میکند که به ترتیب شامل ماه ، روز ، ساعت و دقیقه است._recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; Job, Cron.Yearly(2,4,3,10));در نهایت با استفاده از این Cron ها میتوانید انواع مختلفی از Recurring Job هارا بسازید.4. تسک های Continuations :این نوع از تسک های ، وابسته به تسک های دیگر هستند و بطور کلی وقتی استفاده میشود که ما میخواهیم تسکی را پس از تسک دیگری با یک زمانبندی به نسبت زمان اجرای تسک اول ، اجرا کنیم. برای مثال میخواهیم 10 دقیقه بعد از ثبت نام کاربر برای او ایمیل احراز هویت ارسال شود که شبیه اینکار را در تسک های Delayed انجام داده بودیم. اما همچنین قصد داریم 5 دقیقه بعد از ارسال ایمیل احراز هویت لینک فرستاده شده را منسوخ کنیم. در این سناریو ما دو زمانبندی داریم اول 10 دقیقه بعد از ثبت نام کاربر و دوم 5 دقیقه بعد از اجرای متد اول. https://gist.github.com/sajadkardel/6f007357fb1138d16f77761236d96ed7 برای ایجاد یک Continuations Job باید از متد ContinueJobWith در اینترفیس IBackgroundJobClient  استفاده کنیم و در ورودی اول این متد آیدی تسک ایجاد شده قبلی را پاس دهیم.برخی از نکات و ترفند های HangFire :1. استفاده از Cron Expression در Recurring Job ها :بطور کلی Cron ها ساختاری تعریف شده برای تعیین بازه های زمانی است. Cron اختصار یافته کلمات Command Run On میباشد که به اجرا شدن یک دستور در زمان مشخص اشاره دارد. برای استفاده از آن ابتدا به تعریف این ساختار میپردازیم :  https://gist.github.com/sajadkardel/83d9afe714c732868defdd6c4d886319 این ساختار را از پایین به بالا در زیر برایتان تشریح میکنیم :* * * * *فیلد اول (Minute) : در این فیلد بایستی دقیقه ای مشخص از یک ساعت را وارد کنید.مانند دقیقه 10 (میتوانید محدوده هم تعیین کنید)فیلد دوم (Hour) : در این فیلد بایستی زمان معلوم را با فرمت ساعت وارد کنید.مانند ساعت 7 (میتوانید محدوده هم تعیین کنید) ، مانند ساعات 12-7فیلد سوم (Day of Month) : در این فیلد بایستی یک روز از ماه را وارد کنید مانند روز 15 ام از ماه (میتوانید محدوده هم تعیین کنید)فیلد چهارم (Month) : در این فیلد بایستی یک ماه از سال را وارد کنید مثلا ماه 4 ام(آوریل) (میتوانید محدوده هم تعیین کنید)فیلد پنجم (Day of Week) : در این فیلد بایستی روزی از روز های هفته یا محدوده ای از آن روز ها را تعیین کنید.مانند صفرم هفته که در کشور های اروپایی و آمریکایی معادل روز یکشنبه است.همانطور که میبینید Cron ها دسترسی بهتری از تعیین بازه های زمانی مختلف اراعه میدهند که میتوانید از آن در Recurring متد ها بجای ورودی های Yearly - Monthly - Weekly - Daily - Hourly - Minutly استفاده کنید. در واقع خود این ورودی ها نیز متدی تعریف شده در کلاس Cron هستند که با فراخوانی آن خروجی Cron Expression میسازند و در درون ورودی متد Recurring قرار میگیرند. در ادامه مثالی خواهیم زد تا نیازمندی به Cron Expression ها را بیشتر درک کنید. فرض کنید میخواهید یک زمانبندی داشته باشید که &quot;هر ماه بین بازه 10 ام تا 15 ام ،  بطور روزانه در ساعت 4:00&quot; اجرا شود. اعمال این زمانبندی با متد های معمول در کلاس Cron امکان پذیر نیست اما میتوانید با Cron Expression این را اعمال کنید که به این شکل خواهد بود : 0 4 10-15 * *برای ساخت Cron Expression ها وبسایت هایی وجود دارند که کمک میکنند انواع Cron Expression های پیچیده را طراحی کنیم و با استفاده از آن زمانبندی های دقیق تر و جزئی تر بسازیم. یکی از بهترین وبسایت ها برای اینکار crontab.guru است.پ ن : برای استفاده از Cron Expression در متد های Recurring کافی است بجای ورودی های Yearly - Monthly - Weekly - Daily - Hourly - Minutly ، خود Cron Expression را درون ورودی متد تعریف کنیم : _recurringJobManager.AddOrUpdate(&amp;quottest&amp;quot, () =&gt; job , &amp;quot0 4 10-15 * *&amp;quot );2. متد Trigger : متد Trigger یک متد برای اجرای آنی تسک ها Recurring است که به کمک آن میتوانید این نوع از تسک های را بدون در نظر گرفتن زمانبندی آن در لحظه اجرا کنید و البته تاثیری در دفعات بعدی تکرار نداشته باشد.RecurringJob.Trigger(&amp;quotsome-id&amp;quot);3. تعیین تاریخ انقضا برای  Recurring Job ها : گاهی ممکن است در تسک های Recurring شرایطی پیش آید که برفرض میخواهید کاری را هر ماه انجام دهید اما این تکرار در پایان همان سال تمام میشود. در اینصورت باید یک Expire Time برای متد Recurring خود تنظیم کنیم تا بعد از 12 ماه تکرار در تاریخ 140X/12/30 به پایان برسد. HangFire برای متد های Recurring ورودی با عنوان ExpireTime تعریف نکرده اما میتوان از طریق ایجاد یک زمانبندی Schedule تاریخ مشخصی برای حذف کردن متد Recurring تعریف کرد همانند یک ExpireTime عمل میکند. https://gist.github.com/sajadkardel/0301ec39846292b1429bebe165d38560 با اجرای این متد اول کاری برای تکرار در زمانبندی ماهیانه ایجاد میشود و در متد دوم زمانی برای حذف متد اول مشخص میکند.در آخر امیدوارم این مقاله برایتان مفید واقع شده باشد. میتوانید فیدبکتان را در قالب کامنت یا یک قهوه برایم ارسال کنید. https://coffeebede.ir/buycoffee/sajadkardel مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>Kaveh Kardel</author>
                <pubDate>Sun, 09 May 2021 13:31:48 +0430</pubDate>
            </item>
                    <item>
                <title>نکات Refactoring برای برنامه نویسان C#</title>
                <link>https://virgool.io/dotnetzoom/%D9%86%DA%A9%D8%A7%D8%AA-refactoring-%D8%A8%D8%B1%D8%A7%DB%8C-%D8%A8%D8%B1%D9%86%D8%A7%D9%85%D9%87-%D9%86%D9%88%DB%8C%D8%B3%D8%A7%D9%86-c-qhh3xibz371j</link>
                <description>در این مقاله قصد داریم به بررسی Code Smell ها و راه حل های Refactoring آنها بپردازیم.در این مقاله از GitHub Gist استفاده شده است و لود شدن بخش مربوط به کد ها ممکن است کمی زمانبر باشدوجود متد های بلند و بزرگیکی از نکاتی که هنگام Refactoring باید در نظر داشته باشید شکاندن متد های بزرگ به متد های کوچکتر می باشد. هنگام نوشتن کد معمولا متوجه وجود متد های بلند نمیشویم ولی هنگامی که میخواهیم آنها را Review و Refactor کنیم متوجه میشویم که متد شامل indentation زیاد و در نتیجه فهم آن بسیار سخت و گاهی اوقات غیر ممکن میشود. همچنین متد های بلند اصل Single Responsibility را زیر سوال میبرد. به طور مثال متد زیر را در نظر بگیرید. https://gist.github.com/babaktaremi/57dd6989a291e654ccb967470883ed91 هم اکنون متد ذکر شده هم مسئول خواندن فایل و هم نوشتن محتوایات آن تحت شرایط خاص در کنسول می باشد. میتوانیم این متد را به شکل زیر به چند متد کوچک تر بشکانیم. با این کار هم متد مربوطه خوانا تر می شود و هم قابلیت استفاده مجدد از کد افزایش می یابد. https://gist.github.com/babaktaremi/6a4ff75d0361d785dc51bb4d18ba16e3  https://gist.github.com/babaktaremi/9c32ecdd325c50728f557a473384fa67  https://gist.github.com/babaktaremi/1be060031f5411a74f68af8e9f515cb4 استفاده بیش از حد از if و Conditional Complexityگاهی اوقات یک متد بیش از حد دارای if Statement می باشد که خوانایی کد و Track کردن اینکه کدام شرط به کدام مسیر از کد منجر می شود را دشوار می سازد. که در این حالت کد دارای Indentation بسیار زیاد میشود که آن را بد شکل می سازد. به طور مثال کد زیر را در نظر بگیرید. https://gist.github.com/babaktaremi/a1528ddc3dee06886f9fbd21beeed798  همانطور که میبینید دنبال کردن اینکه کدام شرط به کدام مسیر منجر میشود کار بسیار سختی است. همیشه سعی کنید که تعداد If Statement های تو در تو را به کمترین حالت ممکن برسانید و در صورت نیاز متد را به متد های کوچکتر بشکانید و شرط ها را داخل آن چک کنید.مشکل Primitive Obsessionاین مشکل زمانی رخ میدهد که وروردی و یا خروجی متد های مختلف به جای اینکه یک data structure مناسب (مانند کلاس و...) باشد ، یک نوع primitive مانند int و یا string و... است که باعث ایجاد constraint ها و چک کردن نوع داده و در نتیجه duplicate code می شود. به طور مثال متد AddHoliday(2,3) را در نظر بگیرید. در این متد مشخص نیست که عدد اول ماه است یا عدد دوم. بهتر است که با ساخت Data structure مناسب صریحا نوع تاریخ را مشخص کنیم. پس متد AddHoliday را به شکل زیر Refactor میکنیم. https://gist.github.com/babaktaremi/a541ffa4b5d762306e0df656feeb6c75  نام گذاری ضعیفیکی دیگر از نکات مهم هنگام Refactor کردن کد، استفاده از نام های مناسب برای متغیرهاست. متغیر ها باید دارای نام هایی باشند که:به اندازه کافی درباره کاربرد داده توضیح دهنده باشنداز استاندارد ها پیروی کنند( نام متغیر های محلی بصورت camel case و نام کلاس ها و پراپرتی های پابلیک و متد ها PascalCase باشد و...)نام ها نباید خلاصه شده و مختصر باشند.باید نام گذاری متغیر و متد دقیق  و Self Descriptive باشد.برای مطالعه بیشتر درباره naming guideline ها میتوانید به این لینک مراجعه کنیدکد تکراییکی از مشکلات رایج که هنگام Refactoring باید به آن دقت داشته باشید کد تکراری است. این مشکل اغلب به خاطر Copy/Paste کد بوجود می آید. اصل Don&#x27;t Repeat Yourself و یا DRY بیان میکند که تا جای ممکن باید از کد تکراری  پرهیز کرد و اگر دو متد دارای یک بخش با Logic یکسان هستند، باید آن بخش را به عنوان یک متد مجزا Extract کرد و در هر دو متد استفاده کرد.مارتین فاولر قانون معروفی به نام Rule of Three معرفی کرده است که بیان میکند که اگر کدی 3 بار یا بیشتر تکرار شود باید استخراج و در procedure خود گنجانده شود.یکی از موارد کد تکراری Validation Logic می باشد که در آن اعتبار یک کلاس یا متغیر سنجیده می شود. به مثال زیر دقت کنید. https://gist.github.com/babaktaremi/1ae89bdb5d79d69fb6928ff488c8808e برای بررسی معتبر بودن یک متغیر، بهتر است که کد اعتبارسنجی آن را نوشته و در جاهای مختلف استفاده کنیم. متد بالا را میتوان به شکل زیر بازنویسی کرد: https://gist.github.com/babaktaremi/0702487a19f1cfd5e9225466c3093804 مشکل Hidden Temporal Couplingاین مشکل زمانی رخ میدهد که بین دو متد هیچ رابطه ای به صورت صریحا وجود ندارد ولی ترتیب اجرای متد ها مهم است. متد ها باید تا جای ممکن Side Effect Free باشند( درباره مطالعه  Side Effect میتوانید به این مقاله مراجعه کنید)به طور مثال متدهای زیر را در نظر بگیرید. https://gist.github.com/babaktaremi/93bc35182afc69d323bdc8426bc9e517 در این متد ها هیج راهی برای فهمیدن ترتیب اجرای آنها وجود ندارد و یک Developer باید با آزمون و خطا ترتیب اجرای آنها را بفهمد. میتوانیم از با استفاده از Template Method متد های بالا را در یک کلاس Abstract آورده و به ترتیب آنها را صدا بزنیم. پس کد بالا را به شکل زیر Refactor میکنیم. https://gist.github.com/babaktaremi/55646bd93c9ecb765dd1f08c65e540a1 حال متد BakeProcess صریحا ترتیب لازم برای اجرای متد های ذکر شده را مشخص میکند.یک راه حل دیگر میتواند این باشد که هر متد نتیجه متد قبلی خود را درخواست کند. پس میتوانیم مانند زیر عمل کنیم: https://gist.github.com/babaktaremi/36dbdec2b30ffce85bd1b5a2ef61dd4e مشکل ادغام Query و Command با یکدیگریکی از نکات مهم هنگام Refactoring، جداسازی Query و Modifier آز یکدیگر است. باید سعی شود که Query ها که یک مقدار را برمیگردانند بدون Side Effect باشند و از طرفی Modifier ( یا Command) قابل تشخیص و جداسازی از Query ها باشند چرا که دارای Side Effect هستند. در این رابطه اصل CQRS یا Command Query Responsibility Segregation بیان میکند که هر درخواست باید Command یا Query باشد، مدل های درخواست نیز باید یا Command و یا Query باشند. این به آن معنی است که مدل های پروژه نیز کاملا از هم جدا هستند و هر یک نماینده یک Command و یا Query می باشند. قبلا در رابطه با CQRS مقاله ای تهیه کردم که برای مطالعه میتوانید به لینک های زیر مراجعه کنید. https://virgool.io/dotnetzoom/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%84%DB%8C-cqrs-%D8%A8%D8%AE%D8%B4-%D8%A7%D9%88%D9%84-%D9%85%D9%82%D8%AF%D9%85%D9%87-%D8%A7%DB%8C-%D8%A8%D8%B1-cqrs-pqcvdqzbcend  https://virgool.io/dotnetzoom/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%84%DB%8C-cqrs-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%A7%D9%84%DA%AF%D9%88%DB%8C-mediator-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-mediatr-aavpwkagy2pu  https://virgool.io/dotnetzoom/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%84%DB%8C-cqrs-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%B9%D9%85%D9%84%DB%8C-ccam8lktowgk مشکل متغیر موقتاین مشکل زمانی رخ میدهد که یک متعیر فقط در یک شرایط خاص مقدار دهی میشود و در باقی شرایط مقدار Null و یا پیش فرض خود را دارد. در قطعه کد پایین CalculateBonus تنها وقتی مقدار درست و غیر صفر دارد که بعد از متد CalculateEarnings صدا زده شود: https://gist.github.com/babaktaremi/aea916789c34b009844457bf096fd32d بهتر است که در چنین شرایطی مستقیما پارامتر را به متد پاس داد و یا متد محاسبه Earning را از کلاس Employee جدا کرده و یک instance معتبر از Employee را به آن پاس داد. پس میتوان یک کلاس به شکل زیر ایجاد کرد. https://gist.github.com/babaktaremi/0346cfef593b93e461779bfe630ea6ef در اینجا به جای آنکه یک متغیر که گاهی اوقات مقدار دهی میشود را به متد های مختلف پاس بدهیم، یک instance از Employee که همیشه مقدار دهی شده است را به متد های مختلف پاس میدهیم. که در این حالت مشکل Null و یا مقدار پیش فرض بودن یک متغیر را نخواهیم داشت.مشکل وابستگی پنهاناین مشکل زمانی رخ میدهد که یک کلاس یا متد وابستگی هایی داشته باشد که از طریق Constuructor و یا Parameter خود این وابستگی ها را درخواست نکرده باشد و به نوعی این وابستگی ها را پنهان کرده باشد.به کلاس زیر دقت کنید.کلاس Employee در متد Calculate وابستگی مستقیم به کلاس Calculator دارد که این وابستگی به هیچ وجه از خارج کلاس مشخص نیست. https://gist.github.com/babaktaremi/4f5af0f612f43d4a2d3c9cdd324298f8 بهتر است که کلاس وابستگی خود به کلاس Calculator را صریحا نشان دهد و یک instance از کلاس Calculator را به جای ایجاد کردن، در Construcutor خود درخواست کند. پس کلاس Employee را به شکل زیر Refactor میکنیم. https://gist.github.com/babaktaremi/0ff42c17b93092ff1c2ef601d6b59f3d کلاس های Anemicیکی دیگر از مشکلاتی که هنگام Refactoring به آن برمیخوریم، کلاس های دارای تنها فیلد و پراپرتی و بدون رفتار هستند که معمولا برای انتقال Data از آنها استفاده میشود. این گونه کلاس ها معمولا وابسته به سایر کلاس ها برای اعتبارسنجی و سایر عملیات هستند. به مثال زیر دقت کنید. https://gist.github.com/babaktaremi/67745053a492f69981332cee77848682 این کلاس هیچگونه رفتاری ندارد و وابسته کلاس دیگر برای انجام عملیات می باشد. به طور مثال یک کلاس دیگر میتواند از این کلاس به شکل زیر استفاده کند: https://gist.github.com/babaktaremi/cacd748929c944112c4052ba9b692bb5 میتوانیم متد CalculateInterst را مستقیما درون کلاس Account پیاده سازی کنیم و این ویژگی را وابسته به کلاس Account کنیم. پس کلاس Account را به صورت زیر Refactor میکنیم. https://gist.github.com/babaktaremi/cf5d21ac47d7d6febafec2dac8f244b9 نتیجه گیریدر این مقاله به بررسی 10 نکته برای Refactor کد های C# پرداختیم. به طور کلی رعایت اصول SOLID و استفاده از Design Pattern ها میتواند به داشتن یک کد تمیز کمک کند. خوشحال میشوم که نظرات شما در رابطه با مقاله را مطالعه کنم. همچنین اگر دوست داشتید میتوانید من را به یک قهوه مهمان کنید. https://coffeebede.ir/buycoffee/bobby مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom  </description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Sun, 02 May 2021 12:03:16 +0430</pubDate>
            </item>
                    <item>
                <title>اشتباهات رایج در استفاده از Async/Await + راه حل</title>
                <link>https://virgool.io/dotnetzoom/%D8%A7%D8%B4%D8%AA%D8%A8%D8%A7%D9%87%D8%A7%D8%AA-%D8%B1%D8%A7%DB%8C%D8%AC-%D8%AF%D8%B1-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-asyncawait-%D8%B1%D8%A7%D9%87-%D8%AD%D9%84-ca8xabgfwrqw</link>
                <description>در این مقاله قصد داریم به 7 اشتباه رایج در استفاده از Async/Await بپردازیم و راه حل مناسب را برای آن ارائه دهیم.در این مقاله از Github Gist استفاده شده است و لود شدن بخش مربوط به کدها ممکن است کمی زمانبر باشد. همچنین ممکن است که نیازمند نرم افزار رفع تحریم باشید.1-یک بار async. همواره asyncیکی از اشتباهات رایج هنگام استفاده از TPL این است که یک Task که async میباشد در یک متد sync صدا زده شود. همیشه به یاد داشته باشید که اگر یکجا از متد async استفاده کرده اید همواره آن را در متد های async استفاده کنید و برای متد های sync، معادل async آن را بنویسید.در مثال زیر یک کلاس SomeTask داریم که یک متد async دارد. https://gist.github.com/babaktaremi/ce5e78352a4969983337dd04fe991098 حال در کلاس Main آن را به صورت زیر در متد Main استفاده میکنیم که اشتباه است.چون یک متد async را داخل یک متد sync صدا زده ایم. https://gist.github.com/babaktaremi/61ce26657a1447948f5b4cdc6db2e61c متد Main را به شکل زیر به صورت زیر بازنویسی میکنیم و معادل async آن را مینویسیم. https://gist.github.com/babaktaremi/ebb452cbb21d66570a6e5955b745ec2c 2-استفاده از async voidیکی دیگر از اشتباهات بسیار خطرناک در مورد استفاده از async/await استفاده از async void به جای async Task می باشد. متدی که async void باشد دو مشکل جدی را به همراه خود دارد:1- در سایر متد ها نمی توان آن را await کرد چرا که خروجی آن از جنس Task نیست و در نتیجه ممکن است که جوابی که مد نظر ماست از این متد دریافت نشود.2- در صورت وقوع exception نمیتوان آن را catch کرد چون که خروجی از جنس void می باشد و هیچ مقداری ندارد. و در نتیجه کل برنامه crash میکند.پس بهتر است که جنس خروجی متد های async را از نوع Task قرار دهیم.نکته:تنها یک جا و فقط یک جا مجاز به استفاده از async void هستیم و آن هم موقع هندل کردن Event ها می باشد.در کلاس SomeTask زیر متد DoSomeWork به صورت async void نوشته شده است. https://gist.github.com/babaktaremi/fd627e4c914849f4c07be0b35abb830c اگر دقت کنید در متد Main کلاس Program وقتی که متد DoSomeWork را میخواهیم await کنیم به خطا بر میخوریم. https://gist.github.com/babaktaremi/f4efd37a29b3b1f2492ebb2ec47dc376 برای بر طرف کردن خطا Signature متد را به جای void به Task بر میگردانیم. https://gist.github.com/babaktaremi/fb05176a5987de40179cc3bcadcc6d59 3-استفاده از Task.Run به جای Task.FromResultهمیشه بهتر است وقتی که متد شما async است ولی بدنه آن هیچ عملیات async ای ندارد و نمیخواهید که عملیاتی را به صورت async انجام دهید از Task.FromResult به جای Task.Run استفاده کنید . چرا که استفاده از Task.Run هیچ مزیتی به همراه خود ندارد و تنها یک Thread اضافی از Thread Pool را مصرف میکند(مگر آنکه واقعا نیاز داشته باشید که عملیات در یک Thread دیگر انجام شود که مانعی ندارد).مجددا در متد DoSomeWork اینبار یک مقدار int را بر میگرداند. در روش اول این کار را با استفاده از Task.Run انجام میدهیم که اشتباه است. https://gist.github.com/babaktaremi/daf20daf26c16efc1660c13e3e6e5b3e در روش دوم همانطور که گفته شد به جای استفاده از Task.Run از Task.FromResult و یا Task.CompletedTask برای نوع غیر جنریک Task استفاده میکنیم. https://gist.github.com/babaktaremi/f3648807ff1c9c6154e24e507d020d61 4-استفاده از .Result و .Waitیکی دیگر از اشتباهات رایج، استفاده از Result و Wait می باشد. با اینکار عملیات async به صورت sync اجرا میشود و همچنین یک Thread دیگر برای اجرای عملیات استفاده میشود. یعنی عملا دو Thread برای اجرا به صورت sync مورد استفاده قرار میگیرد. این کار ممکن است که موجب ایجاد بن بست (Deadlock) شود. در این مورد نیز باید قانون &quot;یک بار async همواره async&quot; را رعایت کنیم و متد های async را همواره به صورت async اجرا کنیم. در نظر داشته باشید که در اکثر مواقع میتوان معادل async تمام متد های sync را نوشت.در متد DoSomeWork زیر خروجی آن به صورت Task می باشد. https://gist.github.com/babaktaremi/05dea35b7cdc9eca744c5487ba57bd9d در متد Main زیر برای بدست آوردن نتیجه Task از Result آن استفاده کرده ایم که کار اشتباهی است. https://gist.github.com/babaktaremi/0cdcfcada14da0fd60fb61741a138959 روش درست آن است که متد main را به صورت async در بیاوریم و برای بدست آوردن نتیجه آن را await کنیم. پس متد main را به صورت زیر بازسازی میکنیم. https://gist.github.com/babaktaremi/c212957c92c58fe3cb7e9079598074de 5- عدم استفاده از ()GetAwaiter().GetResultزمانی که نیاز است حتما یک متد async را داخل یک متد sync صدا بزنید، بهتر است که به جای استفاده از .Wait و .Result از ()GetAwaiter().GetResult استفاده کنید. با این کار در صورت وقوع exception به جای برگرداندن Aggregate Exception که مجموعه ای از Exception ها است ، خود Exception را بر میگرداند.6-استفاده بیش از حد از async awaitیکی از اشتباهات رایج استفاده بیش از حد از async await می باشد. در اینجا میتوانیم از تکنیک Task Eliding استفاده کنیم. یعنی به جای اینکه یک Task را await کنیم، خود Task را مستقیما بعنوان خروجی متد برگردانیم و در متد های بالاتر از آن هنگامی که میخواهیم از آن استفاده کنیم، آن را await کنیم. در متد GetGoogleContent زیر یک Task از نوع stringداریم که در آن نتیجه کار await شده است و محتوای html صفحه اصلی Google برگردانده شده است. https://gist.github.com/babaktaremi/47bcc7ff54fd6ddbd36c319a7630c822  به جای اینکار، بهتر است که مستقیما خود Task را برگردانیم و در متد Main آن را await کنیم. https://gist.github.com/babaktaremi/0b10b5a3f8cb2f147fd64f2e3001d70b سپس از این متد در متد main به شکل زیر استفاده میکنیم. https://gist.github.com/babaktaremi/6e83827ef1e429f0fa82f798976e4f26  7-سینک کردن یک متد async در Constructor یک کلاسهمانطور که میدانید سازنده یک کلاس نمیتواند async باشد. اگر نیاز داشته باشیم که یک async call در constructor یک کلاس داشته باشیم، باید از .Wait و یا .Result استفاده کنیم که قبلا به عنوان یک اشتباه رایج به آن اشاره کرده ایم.یک راه حل مناسب این است که از الگوی Factory Method استفاده کنیم. به جای اینکه از Constructor برای ساخت کلاس استفاده کنیم، از یک static method برای ایجاد آن استفاده کنیم.در مثال زیر فرض کنید که کلاس SomeTask برای ایجاد نیاز به یک async call داشته باشد. پس این کلاس به شکل زیر خواهد بود. https://gist.github.com/babaktaremi/253343ee36c546d91414e8efa6d780cc و سپس در متد Main به شکل زیر از کلاس SomeTask استفاده خواهیم کرد. https://gist.github.com/babaktaremi/f68d10354619bc10865ea6990939cbd8 برای حذف .Wait مراحل زیر را انجام میدهیم.1-ابتدا Constructor کلاس را به شکل private تبدیل میکنیم.2- سپس یک متد استاتیک به نام Create که حاوی امضای async می باشد را ایجاد کرده و کلاس را داخل آن New میکنیم.پس کد کلاس SomeTask را به شکل زیر تغییر میدهیم. https://gist.github.com/babaktaremi/446f7f4d3788e47e0c6fc2dbe2c3632d سپس برای استفاده از کلاس SomeTask در متد main، کد کلاس main را به شکل زیر تغییر میدهیم.  https://gist.github.com/babaktaremi/31fb32edecbb327f7b8f2a8583a6ec70 نتیجه گیریدر این مقاله به بررسی 7 اشتباه رایج هنگام استفاده از async/await پرداختیم.به طور کلی توصیه میشود که هر جا توانستید از async/await استفاده کنید و سعی کنید که Blocking Call نداشته باشید و اگر نیاز به نتیجه یک async call داشتید، آن را await کنید. همچنین همواره در async call ها Task و یا نوع جنریک آن Task&lt;&gt; را استفاده کنید.مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Wed, 31 Mar 2021 22:35:37 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش Cache در Asp.Net Core (قسمت دوم : EasyCaching)</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-cache-%D8%AF%D8%B1-aspnet-core-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-ljkjb2vs1xcn</link>
                <description>در این مقاله برای نمایش کد ها از GitHub Gist استفاده شده و ممکن است Load شدن کد ها کمی طول بکشد یا به نرم افزار های رفع تحریم نیاز داشته باشید.در قسمت اول درمورد سیستم Cache دیفالت موجود در Asp.Net Core و مزیت ها و معایب آن گفتیم.  قسمت اول مقاله را میتوانید از این لینک بخوانید. https://vrgl.ir/9mIBO در این قسمت میخواهیم یک پکیج محبوب و کاربردی برای پیاده سازی کش در Asp.Net Core را بررسی کنیم.در دنیای امروز برنامه نویسی ، پکیج ها و فریمورک ها نقش بسیار مهمی را ایفا میکنند بطوری که در بسیاری ازین موارد استفاده از این پکیج ها عمل عاقلانه تری نسبت به دوباره نویسی فیچر های مربوطه است.برای عمل کشینگ در Asp.Net Core نیز پکیج های فوقالعاده ای وجود دارد که در این مقاله به بررسی و استفاده از پکیج EasyCaching میپردازیم.در این پکیج هر یک از متد های موجود در عملیات کشینگ بصورت بهینه ای تعریف شده که قابل استفاده است. سیستمی که این پکیج برای کش کردن داده ها استفاده میکند همان سیستم کش Asp.Net Core هست و بنوعی سوار بر این سیستم قابلیت های بیشتر و بهتری را اراعه میدهد.این متد ها شامل :TrySet/TrySetAsyncSet/SetAsyncSetAll/SetAllAsyncGet/GetAsync(with data retriever)Get/GetAsync(without data retriever)GetByPrefix/GetByPrefixAsyncGetAll/GetAllAsyncRemove/RemoveAsyncRemoveByPrefix/RemoveByPrefixAsyncRemoveAll/RemoveAllAsyncFlush/FlushAsyncGetCountGetExpiration/GetExpirationAsyncRefresh/RefreshAsync(This will be discarded later, just use set directly) یکی از قابلیت های دیگر این پکیج سازگاری آن با انواع Cache Provider های موجود است. بطور خلاصه Cache Provider ها همان اراعه دهندگان حافظه Ram در قالب ها و ابزارهای مختلف هستند. برخی از این ها با داشتن الگوریتم های بهینه تر ، سرعت بالاتری از ردو بدل کردن اطلاعات در Ram را در اختیار ما قرار میدهند و Local بودن یا Distributed بودن را کنترل میکنند. Cache provider های گوناگونی وجود دارد که هریک به شکلی کار میکند ، برای مثال شما میتوانید با Provider ای مستقیما با خود Ram برای Get و Set کردن کش های خود در ارتباط باشید و یا در روشی دیگر از یک دیتابیس(Redis) ، جدا از دیتابیس اصلی برنامه ، که حافظه مصرفی آن Ram هست و منابع حافظه شمارا نیز مدیریت میکند ، برای کش های خود استفاده کنید  و اطلاعات را بصورت ایندکس گذاری شده در Ram ذخیره کنید که به سرعت واکشی آن میفزاید.بطور کل Cache Provider هایی که پکیج EasyCaching با آن ها سازگار است شامل :In-MemoryMemcachedRedis(Based on StackExchange.Redis)Redis(Based on csredis)SQLiteHybridDiskLiteDbیکی دیگر از مزیت های این پکیج سازگاری آن با Serializer های مختلف است. همانطور که میدانید دیتا های ورودی و خروجی در برنامه نیاز به Serialize شدن دارند. وقتی میخواهید دیتایی در دیتابیس ذخیره کنید آن را در قالب یک شی (Model) از کاربر دریافت میکنید و شما باید برای ذخیره این دیتا ، اطلاعات درون شی را به قالبی که قابل ذخیره شدن باشد در آورید که این عمل Serialize نام دارد. دقیقا برعکس این روند بعد از واکشی اطلاعات از دیتابیس ، اطلاعات را در قالب اشیایی که قابل نمایش به کاربر باشد (DeSerialize) در میاوریم.در کش کردن هم چیزی که شما با آن سروکار دارید دیتا است پس برای ذخیره و واکشی این دیتا از هر حافظه ای چه دیتابیس چه Ram باید از یک Serializer استفاده کنید تا عملیات Serialize و DeSerialize را برایتان انجام دهد. Serializer  های مختلفی وجود دارد که بصورت پکیج هایی اراعه شده اند و اما Serializer  هایی که سیستم EasyCaching آن هارا پشتیبانی میکند شامل :BinaryFormatterMessagePackNewtonsoft.JsonProtobufSystem.Text.Jsonدر ادامه به پیاده سازی کش با استفاده از EasyCaching در سه Provider مختلف از این پکیج میپردازیم.1_ پروایدر InMemory :پروایدر InMemory یک سیستم Local Caching را برای ما به وجود میاورد. (در قسمت قبلی مقاله سیستم های Local(InMemory) و Distributed را بررسی کردیم و تفاوت های میان آن ها را گفتیم.)برای استفاده از پروادر InMemory در EasyCaching باید پکیج زیر را نصب کنید :Install-Package EasyCaching.InMemoryدر مرحله بعد کانفیگ های مربوط به این پکیج را در کلاس Startup برنامه خود میاوریم :راحت ترین روش افزودن این پکیج به Startup صرفا افزودن حالت پیشفرض آن به متد ConfigureServices است که به شرح زیر عمل میکنیم : https://gist.github.com/sajadkardel/6a613272d506676611c300d11194ffd2 این حالت از کانفیگ پکیج ، تنظیمات دیفالت خود پکیج را برای برنامه قرار میدهد و شما میتوانید با استفاده از option های دیگر که در متد ()UseInMemory وجود دارد تنظیمات شخصی سازی شده از سیستم کشینگ خود را اعمال کنید.و تمام. هم اکنون میتوان با استفاده از اینترفیس IEasyCachingProvider که این سرویس در اختیارمان قرار داده و عمل تزریق وابستگی آن در کلاس ها و کنترلر های مان دیتای در حال عبور را کش کنیم. متد های موجود در این اینترفیس به شرح زیر میباشد :  https://gist.github.com/sajadkardel/f3cd53e88b3ff39c9e92682193cedeb0 همانطور که قبلا گفته شد سیستم کش با دیتا مرتبط است و نیازمند یک Object Serializer جهت Serialize کردن اطلاعات ورودی و ذخیره آن در Target Storage مشخص شده است.پکیج EasyCaching برای Provider های خود یک Object Serializer دیفالت قرار داده است و تا وقتی که شما آن را طبق نیازی خاص بصورت کاستوم تغییر نداده باشید از آن استفاده میکند.در میان پنج Serializer معرفی شده که EasyCaching آن هارا ساپورت میکند ، BinaryFormatter بصورت دیفالت در همه Provider ها برقرار است و تا وقتی که یک Serializer انتخابی به EasyCaching معرفی نکنید این پکیج از این Serializer استفاده میکند.برای استفاده از Serializer های دیگری که معرفی شده میتوانید از لینک های زیر کمک بگیرید :MessagePackNewtonsoft.JsonProtobufSystem.Text.Json2 _ پروایدر Redis :ردیس یک دیتابیس Key Value محور هست که محل ذخیره سازی آن Ram هست و اطلاعات بصورت موقت در آن ذخیره میشود. بطور خلاصه Key Value یعنی یکبار کلید و مقداری برای آن کلید تعریف میشود و هروقت نام کلید تعریف شده صدا زده شد مقدار نسبت داده شده به آن در اختیار ما قرار میگیرد. برای مثال کلید &quot;Name&quot; و مقدار &quot;James&quot;. با این انتصاب هروقت &quot;Name&quot; فراخوانده شود مقدار &quot;James&quot; را خواهیم داشت. سیستم Key Value بخاطر عدم پیچیدگی و سادگی ای که دارد بسیار سریع عمل میکند و همچنین ایندکس گذاری هایی که ردیس روی دیتا ها انجام میدهد باعث افزایش سرعت آن نیز خواهد شد که ردیس را به سریع ترین دیتابیس Key Value دنیا تبدیل کرده.در اینجا با توجه به قابلیت هایی که ردیس داراست یکی از بهترین گزینه ها برای انتخاب بعنوان فضای ذخیره سازی کش ها بصورت Distributed است. برای استفاده از این دیتابیس قدرتمند ابتدا باید از طریق یکی از روش های معمول اقدام به نصب آن کنید. میتوانید فایل نصبی را از وبسایت رسمی آن دانلود کنید و یا یا با استفاده از Docker اقدام به نصب آن نمایید.پس از نصب این دیتابیس روی سیستم خود ، برای استفاده از آن در EasyCaching ابتدا باید پکیج مورد نیاز را نصب کنید.Install-Package EasyCaching.Redisادامه کار به همان سادگی پروایدر قبلی هست و فقط کافیست EasyCaching و option ردیس را به کلاس Startup اضافه کنید. https://gist.github.com/sajadkardel/fd18a917badcbb544df4cf1ab4007215 با استفاده از متد UseRedis شما قابلیت استفاده از ردیس را در EasyCaching فعال میکنید و سپس باید اطلاعات Host و Port ردیس نصب شده روی سیستم خود را به این متد معرفی کنید.اگر ردیس را بدون تنظیمات شخصی سازی شده و در همان حالت دیفالت خودش نصب کرده باشید Host و Port شما مانند نمونه بالا 127.0.0.1 و 6379 خواهد بود و نیازی به تغییر نیست.در مرحله بعد برای استفاده از پروایدر ردیس ، اینترفیس IRedisCachingProvider در سرتاسر برنامه در دسترس خواهد بود. این اینترفیس علاوه بر اینکه متد های اصلی موجود در EasyCaching را ساپورت کرده ، بخاطر ساختار دیتابیسی که خود ردیس در اختیار ما قرار میدهد قابلیت های بیشتری نیز اراعه خواهد داد. این قابلیت ها خصیصه های ردیس هست چرا که  این دیتابیس هم دقیقا شبیه به ساختار سیستم کش Key , Value را پشتیبانی میکند و در پی آن قابلیت هایی برای مدیریت بهتر کلید ها و مقادیر اراعه میدهد.اینترفیس IRedisCachingProvider شامل تعداد زیادی از متد ها برای پشتیبانی از قابلیت های ردیس است که در ادامه همه آنهارا نام برده و برخی را توضیح مختصری خواهیم داد :متد های Keys https://gist.github.com/sajadkardel/31f37d9bb6817daffe99b41d833c0cf3 متد های String https://gist.github.com/sajadkardel/acc7662d61be50754fa0809c8eac169b متد های Hashes https://gist.github.com/sajadkardel/5d445599d64fed9ddc396b00bf9a3eb9 متد های List https://gist.github.com/sajadkardel/47feecd3c0e3852f2e10fa9f5da82767 متد های Set https://gist.github.com/sajadkardel/fdb87cb498f0e239935c347cba029e14 متد های Stored Set https://gist.github.com/sajadkardel/2d29996d114587c084cfc2d5c5a68108 متد های Hyperloglog https://gist.github.com/sajadkardel/ca13228927ef5e9cf1fdb3a2f0fc862a متد های Geo https://gist.github.com/sajadkardel/1ebea94c35c50e78068c11cbaa776e1f برای اطلاعات بیشتر از متد های دیگر موجود در ردیس میتوانید از این لینک استفاده کنید.3_ پروایدر Hybrid :این پروایدر روشی از کشینگ مابین Local Caching و Distributed Caching را اراعه میدهد و میتوانید از یک پروایدر Local مثل InMemory و پروایدر Distributed مثل Redis همزمان باهم استفاده کنید که در یک کانال باهم و در راستای هم کار میکنند.اما سوال اینجاست که این قابلیت دقیقا چه کاری انجام میدهد؟همانطور که قبلا گفته شد کش In-Memory سرعت بالاتری نسبت به کش Distributed دارد اما دچار معایبی در حالت چند سروری هست که این معایب از جمله حذف شدن دیتا یک سرور در صورت Down شدن آن ، Sync نبودن کش سرور ها باهم دیگر و دو نسخه کش کردن دیتا در هر سرور و موارد دیگر که میتوان نام برد.اما از طرفی کش Distributed مشکلات چند سروری را با قرار دادن یک مرکزیت واحد کش در حافظه شبکه شده سرور ها برطرف میکند و اطلاعات سرور ها از یک منبع خوانده میشود و طبعا مشکلات In-Memory را نخواهیم داشت اما به دلیل ردو بدل شدن دیتا در محیط شبکه و عمل Serialize , Deserialize که هنگام عبور دیتا روی آن صورت میگیرد ، بخشی از سرعت کاهش خواهد یافت و درنهایت Performance کمتری نسبت به In-Memory اراعه میدهد.حالا برای اینکه بتوانیم سیستم کش خودمان را طوری طراحی کنیم که عیب های (Local)In-Memory و Distributed را نداشته باشیم و هم بتوانیم از هریک به شکلی درست استفاده کنیم که هم اطلاعاتمان Sync باشد و هم از سرعت بالای In-Memory برخوردار شویم میتوانیم از پروایدر Hybrid استفاده کنیم.شیوه کار این پروایدر به این صورت است که وقتی برنامه برای بار اول به کش In-Memory درخواستی ارسال میکند و کش مورد نظر در آن وجود ندارد ، برنامه یک درخواست دیگر به کش Distributed ارسال میکند و دیتای مورد نظر را به کاربر بازگشت میدهد و علاوه بر آن یک کپی از کش آن دیتا ، در کش In-Memory هم ایجاد میکند. با این ساختار از دفعات بعد که کاربر درخواستی ارسال کند دیتای درخواستی در In-Memory نیز موجود خواهد بود و سریع تر از بار اول پاسخ را ارسال خواهد کرد.از طرفی نیز وقتی کاربر دیتای جدیدی را ذخیره میکند ابتدا آن دیتا در In-Memory کش شده و سپس با درخواست خود پروایدر در کش Distributed هم اعمال میشود تا در نهایت دیتابیس نیز آن را ذخیره کند.وقتی این اتفاق میفتد پروایدر Hybrid با کمک پکیج Bus.Redis به کش In-Memory سرور های دیگر دستور Pull کردن دیتا کش های جدید را ارسال میکند و در نهایت همه سرور ها نیز به کمک Distributed مرکزی باهم Sync خواهند بود.برای فعال سازی این پروایدر باید پکیج های زیر را در برنامه خود نصب کنید:Install-Package EasyCaching.HybridCacheInstall-Package EasyCaching.InMemoryInstall-Package EasyCaching.RedisInstall-Package EasyCaching.Bus.Redisدر این مجموعه از پکیج ها از یک پروایدر Local(InMemory) و یک پروایدر distributed(Redis) استفاده شده و همانطور که گفته شد مدیریت هماهنگ سازی این دو توسط پکیج دیگری بنامEasyCaching.Bus.Redisصورت میگیرد.تنظیمات فعالسازی این پروایدر هم متشکل از تنظیمات دو پروایدر In-Memory و Redis بعلاوه معرفی این دو به هم در متد UseHybrid خواهد بود. https://gist.github.com/sajadkardel/c357f0f7b6aeab853532924ca62acc47 برای استفاده از این پروایدر باید اینترفیس IHybridCachingProvider را فراخوانی کنیم. متد های موجود در این اینترفیس همان متد هایی است که در اینترفیس IEasyCachingProvider وجود دارد و از نظر نام متد و روش استفاده تفاوتی میان آن نیست.پیشنهاد شخصی در Distributed Cache ها : همانطور که گفته شد Distributed کش ها گزینه مناسب تری برای برنامه های چند سروری هست اما در این حالت مواردی مثل  Round Trip شبکه و جابجایی اطلاعات در این محیط بعلاوه Serialize , Deserialize هایی که باید انجام شود دلیلی میشود تا سرعت آن در پاسخ به درخواست های برنامه نسبت به حالت تک سروری(In-Memory) کمتر باشد. Hybrid Provider یکی از روش های حل این مشکل بوده که معرفی کردیم. اما برای اینکه تیر خلاص را به پیکره سیستم Distributed Cache خود بزنید و تریک فنی آخر را نیز روی آن اجرا کنید پیشنهاد میکنم از پکیج EasyCaching.Extensions.EasyCompressor که بر پایه پکیج EasyCaching نوشته شده استفاده کنید. این پکیج اطلاعات را قبل از کش شدن فشرده سازی میکند و حجم اطلاعات را به طور محسوسی کاهش میدهد که میزان فضای اشغالی Ram را کم کرده و همچنین عمل جابجایی اطلاعات را نیز تسریع میبخشد. میتوانید از این پکیج هم در Redis و هم در Hybrid استفاده کنید. چگونگی استفاده از آن نیز در لینک Github ذکر شده موجود است.معرفی پروژه :تا اینجا با مفاهیمی که برای شروع استفاده حرفه ای از کش در پروژه تان نیاز بود ، آشنا شدید. در پروژه های واقعی میتوانیم از این سیستم به روش های مختلفی در سطوح مختلفی از برنامه استفاده کنیم برای مثال کد های مربوط به عملیات کش را میتوان بصورت ساده در هر کنترلر تزریق و در اکشن ها استفاده کرد یا از لایه کنترلر آن را به لایه سرویس منتقل کرد ، در روشی دیگر میتوانیم یک Attribute برای این عمل در نظر بگیریم و یا اینکه آن را بصورت یک Middleware اختصاصی در برنامه پیاده کنیم. https://github.com/sajadkardel/EasyCaching در این پروژه علاوه بر اینکه سعی کرده ام استفاده از Provider های معرفی شده را در محیط واقعی تر پیاده سازی کنم ، علاوه بر آن در هر پروژه از این Solution کش را به شیوه ای متفاوت در لایه های مختلفی از برنامه قرار داده ام تا شما همراهان بتوانید طبق نیازتان از روشی مناسب و بهینه در پروژه های واقعی خود از آن استفاده کنید. https://coffeebede.ir/buycoffee/sajadkardel مقالات بیشتر در دات نت زوم https://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>Kaveh Kardel</author>
                <pubDate>Mon, 29 Mar 2021 22:26:24 +0430</pubDate>
            </item>
                    <item>
                <title>Razor Class Library چیست؟</title>
                <link>https://virgool.io/dotnetzoom/razor-class-library-%DA%86%DB%8C%D8%B3%D8%AA-tpquj8cv0xjo</link>
                <description>خب سلام. امروز می خوایم یکم درباره razor class library ها صحبت کنیم.razor class library اصلا چیه؟یک library برای asp.net core هست که می تونه شامل pages(razor pages) ، Views ، viewComponents ،controllers و ... بشه. (یک نکته ای که وجود داره اینه که library ها بصورت مستقل اجرا نمیشن (یک فایل dll بهمون میدن و dll اجرایی نیست) بلکه باید داخل یک برنامه دگ استفاده بشن).کلا RCL (razor class library) کار ما رو برای استفاده مجدد از کدهامون ساده میکنه. نکته جالبی که دربارش وجود داره اینه که برنامه ای که داره از یک RCL استفاده میکنه ، این امکان رو داره که view ها (وقتی که بر اساس mvc نوشته شده) و pages ها (وقتی که بر اساس razor pages نوشته شده) رو override کنه. وقتی که یک view یا یک razor page هم در برنامه ما و هم در RCL موجود باشه ، از اون نسخه ای که در برنامه ما هست استفاده میشه .ایجاد پروژه Razor Class Libraryخب برای ایجاد یک پروژه razor class library توی ویژوال استدیو ، مطابق شکل های زیر عمل می کنیم.(RCL توی Asp.net core 2.1 اومد و قاعدتا توی نسخه های قبلی نمیشه ازش استفاده کرد)خب ، نوع پروژه رو razor class library انتخاب میکنیم. بعدش نام پروژه و نام solution رو ست میکنیم و میریم برای مرحله بعد.خب حالا به تصویر پایین نگاه کنید.توی قسمت 1 که نوشتم که مشخصه و ورژن دات نت رو انتخاب می کنیم. ولی اون تیک قسمت 2 رو بنا به نیاز میتونیم بزنیم یا نزنیم.حالا ماجرای این تیک چیه؟ بصورت پیشفرض RCL از razor pages پشتیبانی میکنه. اگر بخواید از views (MVC) هم استفاده کنید ، باید این تیک رو بزنید (هر دو فعال میشن یعنی هم میتونید razor pages استفاده کنید و هم mvc).خب ، بعد از create زدن ، یک پروژه ای مثل شکل زیر برای ما ایجاد میکنه.خب چون من میخوام از MVC استفاده کنم ، پاک میکنم پوشه Areas رو.خب در ابتدا یک پوشه با نام Controllers و یک پوشه با نام views ایجاد میکنم. همینطور برای Assets های مورد استفادم هم پوشه ای با نام  wwwroot ایجاد میکنم.داخل پوشه کنترلر ها ، یک کنترلر با نام sample ایجاد میکنم و view مربوط به اکشن index این کنترلر رو هم در پوشه views ایجاد میکنم. ساختار تا اینجای کار ، همچین حالتی دارهو کنترلر Sample هم به این فرم هست.و view مربوط به اکشن index هم بدین فرمهخب حالا فرض کنید تا همینجا کافیه. بریم از این RCL ای که ساختیم استفاده کنیم.توی همین سلوشن ، یک پروژه Asp.net Core ای ایجاد می کنم با نام DemoRCL.Web.حالا روی Dependecies پروژه ایجاد شدم کلیک راست میکنم و Add projects references رو میزنم و رفرنس میدم به DemoRCL (یعنی همون Razor class library که ایجادش کردیم).حالا پروژه وبی ایجاد شده رو set as startup میکنم (روی پروژه کلیک راست کنید ، گزینه ای با همین نام وجود داره).پروژه رو ران میکنم. با صفحه زیر مواجه میشیم.توی قسمت URL میام و /sample/indexرو وارد میکنم (بر اساس default route خود mvc).نتیجه در تصویر پایین اومده.ریکوست هدایت شده به اون کنترلر و اکشن داخل RCL و View هم ، همونی هست که خودمون نوشتیم. نکته ای که وجود داره این هست که از layout خود پروژه وب من استفاده کرده (توی RCL من براش layout ست نکردم و علت این مورد همینه)اگر الان ما بریم توی پوشه Views مربوط به RCL و یک پوشه به اسم shared بسازیم و layout براش ایجاد کنیم ، باز هم از این layout استفاده نمیشه . توی اوایل همین مقاله بود که گفتم که اگر دو چیز هم نام ،توی RCL و پروژه وب ما وجود داشته باشه ، میاد و نسخه ای که توی پروژه وب ما هست رو استفاده میکنه.خب الان من بجای استفاده از اسم مرسوم Layout_، از LayoutRCL_ استفاده میکنم.و view مربوط به index رو هم بصورت زیر تغییر میدم.حالا پروژه رو ران میکنم و میرم به/sample/indexو میبینیم که تغییر در layout اعمال شدبزارید یک مثال دگ درباره همین قضیه override شدن view ها و page ها بزنم.خب توی RCL توی پوشه views ، ما یک پوشه به اسم sample داریم که یک فایل index.cshtml توشه و از این استفاده داریم میکنیم توی RCL.حالا من میام دقیقا یک پوشه با همین نام و یک فایل با همین نام داخل پوشه views ولی در پروژه وبی خودم ایجاد میکنم.و داخلش هم یک متن ساده مینویسم (متن : این پیغام از سمت پروژه وب است).حالا وقتی برنامه رو ران میکنم و میرم سراغ /sample/indexاین دفعه دگ از View داخل RCL استفاده نمیکنه بلکه میاد و از view داخل خود پروژه وبی ما استفاده میکنه.خب این فولدر sample که ایجاد کردم رو پاک میکنم .خب حالا میخوام Dependency Injection رو نشون بدم. اینجا هم فرقی نداره . یک پوشه Models توی RCL ایجاد میکنم. توش یک کلاس تحت نام UserDataOptions ایجاد میکنم. (قصد دارم از option pattern استفاده کنم).خب حالا به startup پروژه وبی خودم میرم و یک تغییراتی توش اعمال میکنم.خب تغییرات اعمال شده توی اون قسمتی هست که دورش خط قرمز کشیدم .(اگر با options pattern اشنا نیستید روی این لینک کلیک کنید)خب یک تغییری هم توی appsetting میدم.حالا توی SampleController ، در پروژه RCL رو بصورت زیر تغییر میدم.حالا اگر یک بریک پوینت بزارم توی کانستراکتور این کنترلر ، میتونم ببینم که inject بدرستی انجام شده.خب حالا یک نکته دگ بگم و بحث رو ببندم (حداقل برای الان )میخوام از یک تصویر داخل RCL استفاده کنم. یکم این مورد متفاوت هست (بطور کلی استفاده از Assets ها)خب من یک پوشه درست کرده بودم توی پروژه RCL خودم بنام wwwroot. داخل اون یک فولدر دگ ایجاد میکنم به اسم RCLImages و داخل اون هم یک عکس قرار میدم.اصلا با موس نمیشه خط درست درمون کشید:)خب حالا عکس رو میخوام توی صفحه index پروژه RCL نشونش بدم. صفحه رو بصورت زیر تغییر میدمیکم عجیبه مسیر دهی من؟ خب دلیل داره. وقتی که پروژه publish میشه ، تمام assets های داخل پروژه هایی که بهشون رفرنس داریم ، کپی میشن توی فولدر wwwroot اصلی و توی پوشه ای با نام Content_  و یک پوشه هم با نام اون library ایجاد میشه و تمام assets ها داخلش قرار میگیرن. بزارید نشون بدم.اگر پروژه وب رو پابلیش کنید. بعد به پوشه wwwroot ایجاد شده برید.اون موقع با یک همچین چیزی مواجه میشید.که اگر پوشه Content_ رو باز کنید ، اون موقع با پوشه ای که هم نام با RCL شما هست روبرو میشید و در داخل اون، تمام فایل ها و فولدر های موجود در پروژه RCL و در فولدر wwwroot بود ، قرار دارن.خب یادم رفت نتیجه کدی که نوشته بودیم رو نشون بدم.نتیجه اون در پایینهچیز های زیاد تری وجود داره ک اگر عمری بود در قسمت های بعدی میگم.پروژه رو روی گیت هابمم قرار دادم.(این لینک). توی پروژه گیت هابمم ، یک نمونه از override کردن یک فرم و نتیجه اون فرم رو هم قرار دادمموفق باشید.✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>فرشید جهان منش</author>
                <pubDate>Fri, 26 Mar 2021 21:28:43 +0430</pubDate>
            </item>
                    <item>
                <title>تفاوت Re-execute و Redirect در asp.net core</title>
                <link>https://virgool.io/dotnetzoom/%D8%AA%D9%81%D8%A7%D9%88%D8%AA-re-execute-%D9%88-redirect-%D8%AF%D8%B1-aspnet-core-dlktlses6uo7</link>
                <description>خب سلام.توی این مقاله میخوام یکم درباره error handling صحبت کنم. بصورت کلی در asp.net core دو تا میدلویر برای این منظور داریم . یکی DeveloperExceptionPage که در محیط develop ازش استفاده می کنیم چون اطلاعات بهمون درباره خطای که رخ داده میده. (Stack trace - query string - cookies - headers). و اصولا چه از نظر زیبایی و چه از نظر امنیتی درست نیست که اطلاعات خطای درون برنامه خودمون رو به کاربر نشون بدیم.دومی UseExceptionHandler که از این طریق هم میتونیم که صفحه رو به کاربر برگردونیم و توی محیط production ازش استفاده میشه.یک بحث فقط اینجا مطرح میشه و اونم اینه که این دو تا میدلویر ، کاری با status code ها ندارن و کلا روال کارشون ثابته و فرقی براشون بین استاتوس کد 400 و 500 (بطور مثال) وجود نداره.برای حل مشکل بالا میتونیم از میدلویر StatusCodePages استفاده کنیم.خب بطور مثال کد زیر رو در نظر بگیرید.public class HomeController : Controller
{
    public IActionResult Problem()
    {
        return StatusCode(500);
    }  
}توی این کد یک Action تعریف کردیم به نام problem که statusCode 500 رو برمیگردونه. این تابع statusCode داره یک statusCodeResult برمیگردونه و ورودیش یک int هست که همون statuscode هست .(البته میشه object ای هم بهش به عنوان پارامتر دوم پاس داد که اگر این کارو بکنیم نوع بازگشتی ObjectResult میشه). برای نوع بازگشتی این اکشن هم میشه از IActionResult استفاده کرد که والد همه این نوع هاست (حالا ن دقیقا خودش بلکه فرزند هاش ، مثلا StatusCodeResult داره ارثبری میکنه از کلاس ActionResult که خود ActionResult هم داره IActionResult رو پیاده سازی میکنه).خب بریم سراغ ادامه بحث.حالا توی متد Configure می خوایم بریم.public void Configure(IApplicationBuilder app)
{
    app.UseStatusCodePages();

    app.UseStaticFiles();
    app.UseRouting();
    app.UseEndpoints(endpoints =&gt;
   {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapRazorPages();
   });
}  خب چیز خاصی برای توضیح نیست. یک سری میدلویر قرار دادیم صرفا. حالا اگر یک ریکوست به یک محل اشتباه بزنیم ، به صفحه زیر مواجه میشیم.این خطا ، توسط میدلویر UseStatusCodePages ایجاد شده.حالا اگر ریکوست خودمون رو به اکشن Problem ای که ایجاد کرده بودیم بزنیم ، با صفحه دگ ای مواجه میشیم که در زیر می بینید.خب مشخصا می بینیم که این دو خطا متفاوت از هم نشون داده شدن.خب ، الان مشکلی که توی دو تصویر بالا دیدیم چی هست؟صفحه دیفالت ساخته میشه و قابلیت custom کردن رو نداریم. برای حل این مشکل می تونیم از دو تا میدلویر دگ استفاده کنیم. اولی UseStatusCodePagesWithRedirects و دومی UseStatusCodePagesWithReExecute هست. خروجی این دو تا متد تقریبا یکی هست و جفتشون بهمون این اجازه رو میدن که صفحات error شخصی سازی شده ای رو برای کاربر هامون ایجاد کنیم. (ولی بطور معمول از re-execute استفاده می کنیم که جلوتر دلیلش رو میگم).تصویر زیر داخل UseStatusCodePagesWithRedirects  اتفاق می افته.ریکوست به یک محل ارسال میشه از سمت کاربر. اون اکشن ، اکسپشن صادر میکنه (status code 500). بعدش در زمان برگشت ، موقعی که دوباره به میدلویر UseStatusCodePagesWithRedirects   می رسیم ، میاد و اون status code رو از 500 به 302 (ریدایرکت) تغییر میده و آدرس محلی که باید مرورگر کاربر به اون ریدایرکت کنه (یعنی اون صفحه ارور ما ) رو هم داخلش قرار میده. حالا توی مرحله بعدی ، مرورگر بصورت خودکار یک ریکوست به اون صفحه مربوطه میزنه و صفحه شخصی سازی شده ما با Status code 200 به کاربر برگشت داده میشه.در حقیقت ما در روش بالا ، بار اول به کاربر ارور رو نمیدیم. کاربر رو مجبور میکنیم که یک ریکوست دگ هم به سایت بده و بعد ارور رو بهش نمایش میدیم.خب حالا میخوایم بریم سراغ روش دوم یعنی Re-Execute. نحوه کارش در تصویر زیر قابل مشاهده هست.خب توی این روش ، ریکوست از سمت کاربر میاد (به یک اکشنی که اکسپشن قراره صادر کنه برامون) ، بعدش از میدلویر ها میگذره و وارد اون اکشن میشه و اکسپشن صادر میشه . حالا توی مسیر برگشت وقتی به میدلویر UseStatusCodePagesWithReExecute رسید ، این میدلویر دوباره ریکوست رو به همون pipeline برمیگردونه ولی این بار مسیرش رو ، مسیر error ای که ما تنظیم کردیم میده. اون صفحه ارور به درستی ساخته میشه و به همراه status code 200 به کاربر برگشت داده میشه.خب مشخصا این روش بهتره و این کاملا واضح هست.نکته ای که درباره این میدلویر وجود داره اینه که باید ، اونو قبل از میدلویر UseRouting قرار بدید که وقتی ریکوست رو دوباره generate میکنه ، Route مربوط به ارور انتخاب بشه .public void Configure(IApplicationBuilder app)
{
    app.UseStatusCodePagesWithReExecute(&amp;quot/Home/Error&amp;quot, &amp;quot?statusCode={0}&amp;quot);
    app.UseStaticFiles();
    app.UseRouting();
    app.UseEndpoints(endpoints =&gt;
   {
        endpoints.MapDefaultControllerRoute();
        endpoints.MapRazorPages();
   });
}  خب ، کد بالا فقط اون میدلویر اولی جای صحبت کردن داره . به عنوان اولین ورودی بهش محلی که باید بهش ریکوست ارسال بشه رو میدیم (توی مثال بالا کنترلر home و اکشن error) و بعدشم می تونیم بهش کوری استرینگ پاس بدیم. که اینجا ما statusCode رو پاس دادیم. بطور کلی داخل این میدلویر هر جا ما {0} placeHolder رو قرار بدیم، اون رو با StattusCode مربوطه جایگزین میکنهکد زیر هم مربوط به اکشن error هست که ایجاد کردیم.public class HomeController : Controller
{
    public IActionResult Error(int? statusCode = null)
    {
        if (statusCode.HasValue)
        {
            if (statusCode.Value == 404 || statusCode.Value == 500)
            {
                var viewName = statusCode.ToString();
                return View(viewName);
            }
        }
        return View();
    }
}این مقاله ای که خوندید ترجمه (با مقداری تغییر و تحریف در اصل متن و اپدیت کردن بخش هاییش ) از سایت آقای andrewlock بود .امیدوارم مفید براتون بوده باشه.✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>فرشید جهان منش</author>
                <pubDate>Thu, 25 Mar 2021 12:38:45 +0430</pubDate>
            </item>
                    <item>
                <title>Directory browsing چیست؟</title>
                <link>https://virgool.io/dotnetzoom/directory-browsing-%DA%86%DB%8C%D8%B3%D8%AA-cja4qzi3zmi0</link>
                <description>خب سلام. میخوام درباره قابلیت Directory browsing یکم حرف بزنیم. خب این قابلیت کارش اینه که به یوزر ما اجازه بده که بتونه توی فایل ها و فولدر های موجود داخل یک فولدر خاص ، حرکت کنه.منظورم بطور کلی یک چیزی مثل تصویر بالایی هست.این قابلیت بطور کلی غیر فعاله توی asp.net core  ولی وجود داره و در صورت نیاز میشه که فعال بشه.برای فعال کردن این قابلیت ، کافیه توی متد ConfigureServices ، اکستنشن متد AddDirectoryBrowser رو سر IServiceCollection صدا بزنید.services.AddDirectoryBrowser();حالا توی مرحله بعد ، باید توی متد Configure یک میدلویر به pipeline اضافه کنید.app.UseDirectoryBrowser();خب ، وقتی این میدلویر اضافه بشه ، شما با وارد کردن نام دایرکتوری (که داخل پوشه wwwroot هست) می تونید فایل ها و دایرکتوری های دگ ای که داخل همون پوشه هستن رو ببینید و توشون حرکت کنید ولی عملا همه دایرکتوری های موجود توی wwwroot باز هستن و هیچ محدودیتی نیست.خب این رو هم میشه به روش زیر حل کردapp.UseDirectoryBrowser(new DirectoryBrowserOptions(
{
                FileProvider = new PhysicalFileProvider(
               Path.Combine(env.ContentRootPath, &amp;quotwwwroot\\lib&amp;quot)),
               RequestPath = &amp;quot/files&amp;quot
  });کافیه به میدلویر مربوطه ، یک شی از جندس DirectoryBrowserOptions بدید. دو پراپرتی FileProvider و RequestPath رو هم براش ست کنید. FileProvider در اصل از نوع IFileProvider هست که ما اینجا بهش یک شی از PhysicalFileProvider اختصاص میدیم که توی اون ، آدرس اون پوشه ای که قصد داریم اجازه بدیم بشه بهش دسترسی گرفت رو قرار دادیم.و بعدش هم RequestPath که url ای هست که کاربر باید توی مرورگرش بزنه تا بتونه به این فولدر ما دسترسی پیدا کنه.یک چیز دگ هم بگم. هیچ لزومی نداره که حتما اگر نیازه این قابلیت رو فعال کنید ، تصاویر و یا .... رو داخل wwwroot بزارید . میتونید توی هر پوشه دگ ای توی پروژتون بزارید و از همین روش استفاده کنید.این میدلویر هم قبل از میدلویر UseRouting  قرار میگیره.خب در آخر مجددا بگم که این قابلیت استفاده ازش خوب نیست و به دلیل مشکلات امنیتی که میتونه بوجود بیاره غیر فعاله ولی شاید یک موقعی نیاز شد!. کسی چه میدونه!!موفق باشید.✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>فرشید جهان منش</author>
                <pubDate>Thu, 25 Mar 2021 02:02:40 +0430</pubDate>
            </item>
                    <item>
                <title>hsts چیست؟</title>
                <link>https://virgool.io/dotnetzoom/hsts-%DA%86%DB%8C%D8%B3%D8%AA-fodcc2ttnahc</link>
                <description>فرض کنید که به یک رستوران رفتید و قصد دارید از wifi رایگان آنجا استفاده کنید. دقت کردید که رمز wifi با یک برگه به دیوار چسبیده و هر کسی بخواد می تونه از اون استفاده کنه ؟ممکنه همزمان با شما ، یک هکر هم داخل همون رستوران باشه. خب مشخصا اون هکر میتونه تمام اتصالات نا امن رو به سادگی شنود کنه. هکر می تونه تمام ترافیک های شبکه ای که از پروتکل http استفاده می کنن برای هر سایتی و فقط متکی به ریدایرکت کردن کاربر از http به https هستند رو شنود کنه. خب ، اگر دقت کنیم موقعی که این اتفاق می افته دگ اصلا ssl بی معنی میشه و دیتا های ما کاملا در دست هکر هست.این دلیلیه که ما صرفا به ریدایرکت کردن کاربر از http به https بسنده نکنیم و به سراغ چیز دیگه ای بنام hsts بیایم.(البته خود این تبدیل http به https هم زمان بر هست که با HSTS برطرف میشه)سوال اول. HSTS چیه؟کلمه HSTS مخفف http strict transport security  هست.  HSTS یک هدر واکنشی هست که به مرورگر اطلاع میده که وب سایت های فعال شده فقط از طریق HTTPS قابل دسترسی هستند. بعد از فعال شدن HSTS بر روی سایت ، مرورگر مجبور می شود تا فقط به نسخه https دسترسی داشته باشد.سوال دوم.HSTS چگونه کار می کند؟وقتی ما یک آدرس سایت رو در مرورگرمون می زنیم ، عموما Schema اون رو وارد نمیکنیم. در این حالت بصورت پیش فرض ، http در نظر گرفته میشه و یک درخواست http به سرور ارسال می شه. سرور اون درخواست رو ریدایرکت میکنه (استاتوس کد 301) به https . و پس از اون، مجددا مرورگر یک ریکوست به سرور میده ولی این بار بصورت https . در این مرحله ، HSTS یک هدر روی response ای که به کاربر برگردوند قرار میده.Strict-Transport-Security: max-age=31536000; includeSubDomainsاین هدر تعداد دستور العمل به مرورگر ما میده. یکی از اون ها max-age هست به این معنی که از الان ، تا زمانی که اون تایم تموم بشه (یک سال) هر ریکوستی که به سایت مرورگر بخواد بده ، باید روی https باشه. اگر ریکوستی با پروتکل http به مرورگر بیاد ، مرورگر خودش اون رو جایگزین میکنه با https . برای اینکه این موضوع روی ساب دامین ها هم اعمال بشه ، دستورالعمل includeSubDomains هم در هدر قرار میگیره.سوال سوم. آیا HSTS کاملا امن هست؟متاسفانه بار اولی که به وبسایت دسترسی میگیریم ، از HSTS استفاده نمیشه. اگر وب سایت بیاد و هدر HSTS رو به ریکوست http که براش اومده ، اتصال بده ، اون هدر نادیده گرفته میشه چون هکر میتونه خیلی راحت اون هدر رو پاک کنه یا دستکاری کنه (بدلیل http بودن ). بطور کلی هدر HSTS قابل اعتماد نیست مگر زمانی که از HTTPS استفاده بشه.نکته دگ اینکه هر بار که مرورگر هدر مربوط به HSTS رو ببینه max-age رفرش میشه و maximum اون هم دو سال هست. اگر max-age رو برابر 0 بزاریم ، مرورگر از ریکوست های بعدی اینطوری در نظر میگیره که این سایت یک سایت جدید هست و میتونه ریکوست http بهش بده.چجوری از HSTS داخل ASP.Net Core استفاده کنیم؟خب توی مرحله اول ، باید توی متد ConfigureService ، کد زیر رو بنویسیم. services.AddHsts(opts =&gt; {
opts.MaxAge = TimeSpan.FromDays(1);
opts.IncludeSubDomains = true;
});توی بخش configuration چند تا پراپرتی وجود داره که می تونیم ست کنیم (در اصل همون دستور العمل هایی هستن که توی هدر ست میشن و به مرورگر داده میشن).یکیش max-age هست که خب قبلا دربارش صحبت کردیم. اگر مقداری براش ست نکنیم ، مقدار پیش فرضش 30 روز هست. بعدی IncludeSubDomains هست که باز هم قبلا دربراش صحبت شد. اگر ست نشه پیشفرضش false هست .مورد بعدی ExcludeHosts هست که اون هاست هایی که هدر hsts نباید ست بشه براشون رو مشخص میکنه. بصورت پیش فرض داخلش localhost و 127.0.0.1 و [1::] هست یعنی برای هیچ کدوم از اینها هدر hsts ست نمیشه . میتونیم خودمون هم هر هاستی که بخوایم رو اضافه کنیم.حالا میریم سراغ متد configure .طبق همین pipeline ای که مایکروسافت در داکیومنتش قرار داده رفتار می کنیم.(الان محل بحث ما فقط https و HSTS هست).اول میدلویر HSTS اضافه میشه و بعد از اون میدلویر مربوط به ریدایرکت به https. فقط نکته ای که وجود داره اینه که نیاز نیست HSTS رو برای بخش development قرار بدید (اصلا کار هم نمیکنه چون بصورت پیشفرض سر لوکال هاست و ... که بالاتر گفتم کار نمیکنه و اون هدر مربوطه رو ست نمیکنه).فقط برای production قرارش بدید.میدلویر مربوط به HSTS اسمش UseHsts هست و میدلویر مربوط به ریدایرکت از http به https ، اسمش UseHttpsRedirection هست.موفق باشید.✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>فرشید جهان منش</author>
                <pubDate>Tue, 23 Mar 2021 12:44:42 +0430</pubDate>
            </item>
                    <item>
                <title>آموزش Caching با استفاده از Redis در ASP Net Core</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-caching-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-redis-%D8%AF%D8%B1-asp-net-core-l77s3235bd6t</link>
                <description>در این مقاله قصد داریم به بررسی دیتابیس Redis و Cache کردن داده در آن به وسیله ASP Net Core بپردازیمدر این مقاله از Github Gist استفاده شده است و لود شدن بخش مربوط به کد ها ممکن است کمی زمانبر باشد. همچنین ممکن است که نیازمند نرم افزار رفع تحریم باشیدآشنایی با Redisردیس یک دیتابیس NoSQL می باشد که از قانون ACID به صورت کامل پیروی نمی کند. یعنی داده در آن Durable نیست و ممکن است که در صورت قطعی برق یا System Crash داده موجود در آن از بین برود(هرچند که با اعمال تنظیماتی میتوان بوسیله آن هرچند ثانیه یکبار داده را روی دیسک ذخیره کرد). Redis داده را به صورت Key/Value ذخیره میکند و به علت سرعت بسیار خوب گزینه مناسبی برای Cache کردن داده و استفاده از آن به صورت متوالی می باشد.راه اندازی Redis در محیط ویندوزبهترین گزینه برای راه اندازی Redis در محیط ویندوز استفاده از داکر می باشد. در cmd دستور زیر را برای نصب ایمیج Redis اجرا میکنیم. دقت داشته باشید که برای استفاده از Docker نیازمند نرم افزار رفع تحریم هستید.docker pull redisپس از مدتی، Image مربوط به Redis دانلود و نصب می شود.ساخت Container برای Redisپس از دانلود و نصب Image مربوط به Redis ، برای استفاده از آن باید اقدام به ایجاد Container بکنیم. نکته مهم هنگام ایجاد Container ، ایجاد پسوورد برای آن است که یک نکته امنیتی مهم محسوب میشود.دستور زیر را در محیط cmd اجرا میکنیم.docker run -p 9191:6379 -d --name RedisCache redis --requirepass &amp;quot123456&amp;quotدر اینجا یک Container از Redis با پورت 9191 و نام RedisCache می سازیم و پسوورد 123456 را برای آن تنظیم میکنیم. سپس دستور زیر را در محیط cmd اجرا میکنیم تا کانتینر ساخته شده شروع به کار کندdocker start RedisCacheبرای تست Redis میتوان با دستور زیر به cli در Redis دسترسی پیدا کرد.docker exec -it RedisCache redis-cliدقت کنید که RedisCache نام کانتینر ساخته شده می باشد.سپس با استفاده از دستور زیر در Redis پسووردی که برای آن تنظیم کرده ایم را وارد میکنیم auth 123456در اینجا ما پسوورد 123456 را برای Redis ست کردیم.با استفاده از دستور زیر میتوانیم از صحت کارکرد Redis مطمئن شویمpingاگر در جواب PONG دریافت کردیم یعنی همه چیز به درستی تنظیم شده است.هم اکنون همه چیز برای استفاده از Redis در ASP Net Core آماده است.استفاده از Redis در ASP Net Coreپس از ایجاد پروژه نمونه ASP Net Core ، اقدام به نصب پکیج StackExchange.Redis میکنیم.برای نصب این پکیج، از طریق  Package Manager Console دستور زیر را اجرا میکنیم.Install-Package Microsoft.Extensions.Caching.StackExchangeRedis -Version 5.0.1برای استفاده از Redis در ASP Net Core نیاز است که تنظیمات زیر را در Startup و در قسمت ConfigureServices اعمال کنیم. https://gist.github.com/babaktaremi/40373d3200e8574e2334843ba4448599 در قسمت Configuration کانکشن Redis و همچنین پسووردی که برای آن تنظیم کرده ایم را قرار میدهیم. قسمت InstanceName بیان میکند که با چه کلید واژه ای قرار است که Value های مربوط به Redis ساخته شوند و در واقع نام پیشوند برای Key های داده هایی که قرار است در Redis ذخیره شوند را تنظیم میکند که آن را خالی میگذاریم و کلید ها را خودمان می سازیم.ساخت مدل پروژهدر مسیر پروژه یک پوشه با نام Model ایجاد میکنیم و در آن یک کلاس با نام Book ایجاد میکنیم.کلاس Book را به شکل زیر می سازیم. https://gist.github.com/babaktaremi/7de13b748bc5e7433141678d92db31bb سپس به سراغ ساخت BookController می رویم و یک API Controller به شکل زیر می سازیم. https://gist.github.com/babaktaremi/7f10d05343abe4122c7616a53ba7b35d ذخیره سازی داخل Redis Cacheبرای دسترسی به Redis نیاز به یک Instance از IDistrubtedCache داریم که آن را در کنترلر Inject میکنیم. https://gist.github.com/babaktaremi/87cbb56d16d5296c62d6264dba137472 برای ذخیره سازی یک نمونه Book در Redis یک اکشن با نام CreateBook می سازیم. دقت داشته باشید که Value ذخیره شده در Redis باید به صورت Byte Array باشد. https://gist.github.com/babaktaremi/dd96736c49b9db35f8301cf0e695aadc در قسمت بالا و در Entry Options برای Expire شدن Cache آن را به صورت Sliding تنظیم کردیم که با هربار دسترسی به Cache مدت زمان Expiration آن تمدید شود و یک روز به Expiration Duration آن اضافه شود.سپس این اکشن را به وسیله Swagger تست میکنیم.گرفتن تمامی Book ها از Redisبرای گرفتن تمامی Instance های ساخته شده و ذخیره شده در Redis نیاز است که ابتدا تمامی کلید های ذخیره شده در Redis را لیست کنیم. برای این کار نیاز به یک Instance از Connection Multiplexer داریم. یکی از Best Practice ها هنگام استفاده از Redis آن است که این Instance در سیستم به صورت Singleton رجیستر شود. پس آن را به صورت زیر در سیستم رجیستر میکنیم. https://gist.github.com/babaktaremi/49a132d6fc6fa6bd28be97e449b9204f حال Constructor کنترلر را به صورت زیر آپدیت میکنیم https://gist.github.com/babaktaremi/551b727b49cebb7efdeb47ac09ad9230 سپس با استفاده از پترن زیر تمام کلید های موجود در Redis که با Book_ شروع میشوند را لیست میکنیم و مقادیر آن را deserialize کرده و در لیست میریزیم. https://gist.github.com/babaktaremi/bbb52c8367948270a67d570bfc051490 سپس این اکشن را بوسیله Swagger تست میکنیم.گرفتن یک Book از Redisدر قسمت قبل یک Instance از Book را با کلید ISBN آن در Redis ذخیره کردیم. حال به وسیله همین کلید همان Instance را بازیابی میکنیم. https://gist.github.com/babaktaremi/6305e629f81a11f020f463fd860ef44a سپس بوسیله Swagger این اکشن را تست میکنیم.حذف یک Book از Redisبرای حذف یک Instance از Book از متد RemoveAsync به صورت زیر عمل میکنیم و به آن کلیدی که برای هر Book تعریف کرده ایم را پاس میدهیم. https://gist.github.com/babaktaremi/d280e014c3ba90bd1932e365f797c68d سپس این اکشن را به وسیله Swagger تست میکنیمسپس مجدد تمام Book ها را واکشی میکنیم. مشاهده میشود که Book مورد نظر با موفقیت پاک شده استنتیجه گیریدر این مقاله به طور اجمالی با caching در Redis آشنا شدیم. به طور کلی Distributed Caching می تواند در مقیاس بالا به افزایش پرفرمنس کمک کند و یکی از ابزار های بسیار کاربردی آن Redis می باشد. اگر به کد های این مقاله نیاز داشتید میتوانید آن را از لینک زیر دریافت کنید. همچنین خوشحال میشوم که نظرات خود را در رابطه با این مقاله مطرح کنید. https://github.com/babaktaremi/Sample-Caching-Redis مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Thu, 18 Mar 2021 11:15:39 +0330</pubDate>
            </item>
                    <item>
                <title>اهمیت Side-effect Free و Idempotency در کدنویسی</title>
                <link>https://virgool.io/dotnetzoom/%D8%A7%D9%87%D9%85%DB%8C%D8%AA-side-effect-free-%D9%88-idemponency-%D8%AF%D8%B1-%DA%A9%D8%AF%D9%86%D9%88%DB%8C%D8%B3%DB%8C-gazelp35o4zw</link>
                <description>اهمیت Side-effect Free و Idemponency در کدنویسیبرنامه نویسی Functional Programming (به اختصار FP) در تعریف، یک Paradigm (پارادایم) برنامه نویسی هست که در اون برنامه ها به کمک ترکیب توابع ساخته میشن. (چه تعریف گنگ و غیر ملموسی!)از اونجایی که &quot;پارادایم&quot; کلمه غیر ملموسی هست و اگر اولین بارتون باشه که اون رو میشنوین احتمال زیاد مفهومش رو متوجه نمیشین اینطور تعریفش میکنم: پارادایم برنامه نویسی یعنی یک روش یا رویکرد برنامه نویسی.این رویکرد مفاهیم مختلفی رو داخل خودش داره که اینجا نمیخوام واردشون بشم. چیزی که اینجا میخوام روش تاکید کنیم 3 مفهوم زیر هست:1- Side-effect Free2- Idempotent3- Pure Functionsمفهوم Side-effect Freeاین مفهوم به این اشاره میکنه که یه تابع (متد) نباید &quot;تاثیر جانبی&quot; روی بقیه قسمت های برنامه داشته باشه. خب این یعنی چی؟ کی ما تاثیر جانبی رو بقیه میذاریم؟وقتی متد ما یک shared state تغییر رو بده. shared state به معنی متغیر/مقدار ایی هست که بین چندین تابع یا قسمت مختلف برنامه مشترک هست.پس در واقع وقتی یک تابع، متغیری یا مقداری را که بین چندین تابع یا قسمت های مختلف برنامه مشترک هست رو &quot;تغییر&quot; بده یعنی side-effect اتفاق افتادهدر کنار این مفهوم، مفهوم دیگری نیز وجود داره به نام Avoiding Shared State که تاکید میکنه از ایجاد و استفاده از shared state ها خودداری کنیم. وجود shared state خواسته یا ناخواسته باعث بروز side-effect میشه. پس خودداری از اون باعث میشه به side-effect نیز برنخوریم یا کمتر بر بخوریمچه مزایایی داره؟هرچه کد شما side-effect کمتری داشته باشه، احتمال وقوع باگ کمتر میشه. از طرفی چون تابع ما قرار نیست یک share-state رو تغییر بده، احتمال قوع مشکلات همزمانی هم کاهش پیدا میکنه و نیز با اطمینان بیشتری میتونین توابع تون رو به صورت parallel اجرا کنین.همچنین به ایزوله بودن متد های شما کمک میکنه که این موضوع مخصوصا توی تست نویسی بسیار حائز اهمیت هستنکته:هر برنامه &quot;باید&quot; side-effect داشته باشه. در نهایت شما میخواین دیتا رو داخل دیتابیس یا فایل یا... (که یک shared-state) محسوب میشه ذخیره کنین. پس نمیشه side-effect رو کاملا حذف کرد.ولی نکته اصلی اینجاست که باید side-effect ها رو شناسایی کنین، اونها رو محدود کنین و از پراکنده شدنشون توی کد ها جلوگیری کنین.مفهوم Idempotentاین مفهوم به این امر اشاره میکنه که زمانی توابع ما  Idempotent هستند که اگر اونها رو هرچند بار هم با مقادیر ورودی ثابت و مشابه فراخوانی کنیم همیشه نتیجه یا خروجی یکسان و ثابتی داشته باشهمثلا تابعی که یک مقدار Random رو برمیگردونه Idempotent نیست چرا که هربار نتیجه اش متفاوته؛ همینطور تابعی که DateTime.Now رو برمیگردونه.مثال کاربردیش توی دنیای واقعی، متدی میشه که وظیفه حذف یک رکورد رو داره. به این صورت که (مثلا از ORM EF استفاده میکنه و) ابتدا رکورد رو با id مورد نظر واکشی میکنه سپس اون رو به متد Remove میده و SaveChanges فراخوانی میشهخب بار اولی که این متد رو با id برابر با 100 فراخوانی کنیم اون سطح حذف میشه ولی بار دومی که اون رو &quot;با همین id&quot; فراخوانی میکنیم به exception بر میخوره چرا که دیگه اون رکورد وجود نداره که بخواد حذفش کنه (موقع find، مقدار اون رکورد null هست و متد Remove با ورودی null خطا میده)ولی همین متد رو اگر به این صورت پیاده سازی کنیم که قبل از حذف کردن، چک کنه که اگر این رکورد وجود نداره کاری انجام نده، متد ما Idempotent میشه چرا که هر چندبار فراخوانی اون، یک نتیجه رو داره و اون هم اطمینان از حذف اون رکورد هستنکته:مفهوم Idempotent بودن توی معماری Event-Driven و الگوی Pub/Sub هم اهمیت بسیار بالایی داره. از اونجایی که یک Event یا Message ممکنه به هر دلیلی &quot;بیش از یکبار&quot; توسط Subscriber ها پردازش بشه. باید اطمینان داشته باشیم که نتیجه یکسانی داره و عملکرد سیستم رو تحت تاثیر نمیگذاره (مثلا فرض کنید برای یک سفارش، دو تا فاکتور برای مشتری ثبت بشه!)نمیخوام خیلی وارد روش های Idempotency بشم فقط اینکه معمولا 2 روش وجود داره:1- طوری پیاده سازی کنیم که چند دفعه اجرا شدنش توی عملکردش تاثیری نداشته باشه2- یک flag ایی رو به ازای هر Message داشته باشیم که وقتی اون Message پردازش میشه اون رو true کنیم و دفعات بعدی با چک کردن این flag و متوجه شدن از اینکه قبلا پردازش شده، دیگه پردازشش نکنیم (در این حالت ممکنه استفاده از تکنیک های synchronization مانند locking لازم باشه)چه مزایایی داره؟باعث Reliability (قابل اعتماد) تر شدن توابع مون میشه چون چندین بار اجرا، نتایج یکسانی میدهتست نویسی رو راحت تر میکنه چون با پارامتر های یکسان، هر بار خروجی متفاوتی نمیدهمفهوم Pure Functionsاین مفهوم تعریف خیلی ساده ای داره، تابعی Pure Function (خالص) هست که هم Side-effect Free باشه هم Idempotent. در غیر این صورت میشه Impure Function (تابع ناخالص)مزیت هاشم که واضحه، مشخصا مزایای جفتشون رو داره.جمع بندیمفاهیم Functional Programming الزاما نیاز به استفاده از زبان های FP (مثل FSharp یا Scala یا Haskell) ندارن. خیلی از اون مفاهیم به راحتی داخل زبان های دیگه و رویکرد Object Oriented Programming نیز قابل استفاده و بسیار مفید هستند و باعث Maintainability و Reliability میشن (نگهداری بهتر کد ها و قابل اعتماد تر بودنشون)توصیه میکنم که اون مفاهیم رو یاد بگیرید و بسته به نیازتون توی کدنویسی های OOP تون هم ازش استفاده کنیدمقاله زیر که این مفاهیم رو به خوبی بررسی کرده و مطالعه اون خالی از لطف نیست https://the-dr-lazy.github.io/understanding-side-effect-the-right-way مقالات بیشتر کانال تلگرام دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>محمد جواد ابراهیمی</author>
                <pubDate>Sun, 14 Mar 2021 13:18:48 +0330</pubDate>
            </item>
                    <item>
                <title>C# 9.0: init-only - ایجاد خصوصیات تغییر ناپذیر بدون سازنده</title>
                <link>https://virgool.io/dotnetzoom/c-90-%D9%88%DB%8C%DA%98%DA%AF%DB%8C-init-only-%D8%A7%DB%8C%D8%AC%D8%A7%D8%AF-%D8%AE%D8%B5%D9%88%D8%B5%DB%8C%D8%A7%D8%AA-%D8%AA%D8%BA%DB%8C%DB%8C%D8%B1%D9%86%D8%A7%D9%BE%D8%B0%DB%8C%D8%B1-%D8%A8%D8%AF%D9%88%D9%86-%D8%B3%D8%A7%D8%B2%D9%86%D8%AF%D9%87-kl7pfwicfwta</link>
                <description>در نوشته قبلی، شما با جملات سطح-بالا C# 9.0 آشنا شدید. در این نوشته شما با ویژگی دیگری از C# 9.0 آشنا می‌شوید که ویژگی init-only نامیده می‌شود.در نوامبر 2007 مایکروسافت C# 3.0 را با انتشار NET Framework 3.5. معرفی کرد. C# 3.0 بسیاری از مفاهیم جدید، به عنوان مثال زبان پرس و جو یکپارچه (LINQ) را ارائه داده است. همچنین مفهوم قدرتمند مقداردهی اولیه اشیاء را معرفی کرد. این‌ها پایه و اساسی برای ویژگی init-only هستند که با C# 9.0 معرفی شده است، بنابراین بیایید با مقداردهی اولیه اشیاء شروع کنیم.درک مقداردهی اولیه اشیاءفرض کنید شما یک کلاس Friend با دو خصوصیت FirstName و LastName دارید:به طور سنتی، می توانید با فراخوانی سازنده پیش فرض و پس از آن مقداردهی اولیه خصوصیات، یک شئ از Friend را مانند زیر ایجاد کنید:به جای سه عبارت فوق، می‌توانید از یک دستور با مقداردهنده اولیه شئ، همانند تکه کد زیر استفاده کنید. مقداردهی اولیه شئ همچنین سازنده پیش فرض را فراخوانی می‌کند و سپس با فراخوانی setter دو خصوصیت FirstName و LastName را مقداردهی اولیه می‌کند:ایجاد خصوصیات غیرقابل تغییرمشکلی که در مقداردهی اولیه اشیا وجود دارد این است که به شما امکان ایجاد خصوصیات تغییرناپذیر را حداقل تا قبل از C# 9.0 نمی‌دهد. خصوصیات غیرقابل تغییر خصوصیاتی هستند که پس از ایجاد شئ، نمی‌توانید آن را تغییر دهید. اما در این مورد، شما می‌توانید به عنوان مثال خصوصیت FirstName یک Friend را در هر زمان پس از ایجاد شئ Friend تغییر دهید. تکه کد زیر این را نشان می‌دهد:برای ایجاد خصوصیات تغییر ناپذیر، باید یک سازنده مانند تکه کد زیر ایجاد کنید که firstname و lastname را به عنوان پارامتر در نظر بگیرد. توجه داشته باشید که من setter‌های خصوصیت‌ها را حذف کرده‌ام، این بدان معنی است که پس از مقدار دهی اولیه شئ Friend نمی‌توان خصوصیت‌ها را تغییر داد. تعریف فقط یک getter با Auto property به اصطلاح get-only Auto Property نامیده می‌شود. این یک ویژگی است که با 6.0 #C و NET Framework 4.6. در سال 2015 معرفی شده است. Get-only Auto Properties می‌توانند مستقیماً یا مانند تکه کد زیر در سازنده مقداردهی اولیه شوند. این منطق همان فیلدهای فقط‌خواندنی (readonly) است؛ همچنین می‌توان آن‌ها را مستقیماً یا در یک سازنده مقداردهی اولیه کرد.با کلاس Friend که در بالا تعریف شده است، پس از ایجاد شئ Friend نمی‌توانید خصوصیت FirstName یا LastName را تغییر دهید. در زیر این را امتحان می کنم، می‌بینید که خطایی مشاهده می‌شود که می‌گوید خصوصیت FirstName فقط خواندنی است.اکنون مشکل خود را حل کردیم، خصوصیت‌های FirstName و LastName غیرقابل تغییر هستند. اما برای رسیدن به این هدف، ما باید یک سازنده تعریف می‌کردیم.با C# 9.0 می‌توانید خصوصیات تغییر ناپذیری بدون سازنده ایجاد کنید: این مورد با ویژگی init-only ممکن می‌شود.مفهوم ویژگی init-only در C# 9.0در تکه کد زیر یک کلاس Friend را می‌بینید که کاملاً مشابه کلاس Friend تعریف شده در تکه کد قبلی است. و دقیقاً همان کار را می‌کند. اما آیا می‌توانید تفاوتی را مشاهده کنید؟تفاوت در کلمه کلیدی init است که برای Auto Properties استفاده می‌شود. کلمه کلیدی init برای تعریف نوع خاصی از set accessor استفاده می‌شود (این بدان معنی است که اگر از init و set در یک Auto Properties تک استفاده کنید خطا می‌دهد). این خصوصیات اکنون اصطلاحاً به ویژگی init-only گفته می‌شوند. این بدان معناست که شما فقط می‌توانید این خصوصیات را در سازنده مانند تکه کد بالا، یا مستقیماً مانند تکه کد زیر...... یا *drumrolls*… در مقداردهی اولیه شئ ذکر کنید. این باعث می‌شود سازنده‌ای که در بالا استفاده کردیم برای تعریف خصوصیات تغییرناپذیر غیر ضروری باشد. بیایید به این نگاه کنیم.ویژگی init-only و مقداردهی اولیه شئدر تکه کد زیر یک کلاس Friend با ویژگی init-only و بدون سازنده مشاهده می‌کنید. در واقع، این کلاس Friend دقیقاً همان کلاس Friend نشان داده شده در ابتدای این نوشته است. تنها تفاوت در این است که ما از کلمه کلیدی init به جای کلمه کلیدی set برای Auto Properties استفاده کردیم، که ویژگی init-only آن‌ها را می‌سازد.همانطور که قبلاً ذکر شد این بدان معناست که، می‌توانید خصوصیات را فقط در یک سازنده، مستقیماً یا در مقداردهی اولیه شئ، مقداردهی کنید. کلاس Friend ما سازنده ندارد، بنابراین اجازه دهید از مقداردهنده اولیه شئ استفاده کنیم:به خوبی کار می‌کند. اما اکنون نکته مهم این است که پس از ایجاد شئ نمی‌توان خاصیت را تغییر داد. و این قدرت ویژگی init-only است. در تصویر زیر سعی می‌کنم خصوصیت FirstName را بعد از مقداردهی اولیه شئ Friend تنظیم کنم. همانطور که مشاهده می‌کنید، با خطایی روبرو می‌شوم که می‌گوید ویژگی init-only خاصیت FirstName را فقط می‌توان در مقداردهنده اولیه شئ، سازنده یا init accessor اختصاص داد.مقادیری که برای ویژگی init-only لازم نیستلازم به ذکر است که مقادیر برای ویژگی init-only لازم نیستند، آن‌ها اختیاری هستند. همچنین می‌توانید همانطور که با Auto Properties عادی انجام می‌دهید، فقط خصوصیت‌های مورد نظر خود را تنظیم کنید. به عنوان مثال فقط می‌توانید خصوصیت FirstName یک شئ Friend را در تکه کد زیر تنظیم کنید، که کاملاً معتبر است. اما از آنجایی که ما از ویژگی init-only استفاده می‌کنیم، بعد از این دستور شما دیگر فرصتی برای تنظیم خصوصیت LastName ندارید، زیرا خصوصیت‌ها غیرقابل‌تغییر هستند:تنظیم فیلد‌های فقط‌خواندنی (Readonly)همانطور که در حین مقداردهی اولیه شئ، init accessor از ویژگی init-only فراخوانی می‌شود، امکان تنظیم فیلد فقط‌خواندنی (readonly) در init accessor مجاز است، دقیقاً به همان روشی که می‌توانید آن‌ها را در سازنده تنظیم کنید. اگر می‌خواهید مقدار خصوصیت اختصاص داده شده را بررسی کنید، این کار می‌تواند مفید واقع شود. به عنوان مثال اختصاص دادن فضای خالی (whitespace) یا null برای خصوصیت‌های FirstName و LastName معنی چندانی ندارد. بنابراین، می‌توانید کلاس Friend را مانند زیر ایجاد کنید و فیلد‌های فقط‌خواندنی (readonly) را در init accessor‌های خصوصیت‌ها قرار دهید. خصوصیات هنوز تغییرناپذیر هستند، اما اگر در مقداردهی اولیه شئ فضای خالی (whitespace) یا null اختصاص دهید - این تنها راه تنظیم خصوصیات آن کلاس است زیرا هیچ سازنده‌ای تعریف نشده است - یک ArgumentExceptionدریافت می‌کنید.خلاصهویژگی init-only، یک ویژگی قدرتمند است. آن‌ها به شما امکان می‌دهند خصوصیات تغییرناپذیر را بدون تعریف سازنده‌ای که مقادیر خصوصیت اولیه را می‌گیرد، ایجاد کنید. در حالی که تنظیم ویژگی init-only از سازنده کار می‌کند، شما همچنین می‌توانید ویژگی init-only را با مقداردهنده اولیه شئ تنظیم کنید، و بعداً نمی‌توانید آن‌ها را تغییر دهید، زیرا غیرقابل‌تغییر هستند. اگر قصد دارید با داده‌های تغییرناپذیر در #C کار کنید، این یک ویژگی بسیار مفید است.علاوه بر ویژگی init-only، در C# 9.0 تعداد بیشتری ویژگی برای کار با داده‌های تغییرناپذیر وجود دارد: در نوشته بعدی، شما در مورد انواع Record یاد خواهید گرفت.Source: Thomas Claudius Huber - C# 9.0: Init-only Properties – Create Immutable Properties Without Constructor Boilerplate✅مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>سپهر اسلامی</author>
                <pubDate>Sat, 27 Feb 2021 23:18:29 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی عملی CQRS- بخش سوم: پروژه عملی</title>
                <link>https://virgool.io/dotnetzoom/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%84%DB%8C-cqrs-%D8%A8%D8%AE%D8%B4-%D8%B3%D9%88%D9%85-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%B9%D9%85%D9%84%DB%8C-ccam8lktowgk</link>
                <description>در این بخش به بررسی و پیاده سازی یک پروژه عملی ساده با CQRS و SQL Server و Mongo DB می پردازیمدر این پروژه قرار است با دو دیتابیس کار کنیم. ابتدا توسط یک Command دیتا را با EF Core در دیتابیس SQL Server ذخیره میکنیم و سپس یک Event ایجاد میکنیم. این Event توسط یک Background Service دریافت شده و دیتا را داخل Mongo DB ذخیره میکند. سپس هرجا که نیاز به خواندن و واکشی داده داشتیم، از داده موجود در Mongo DB استفاده میکنیم.در این مقاله از Github Gist استفاده شده است و لود شدن بخش مربوط به کد ها ممکن است کمی زمانبر باشد. https://vrgl.ir/01pUA  https://vrgl.ir/0a7QA آشنایی با MongoDBیک دیتابیس NoSQL می باشد که داده را به جای اینکه به صورت جدولی و با ردیف های مختلف ثبت کند، آنها را در کالکشن هایی به صورت document ذخیره میکند. در Mongo DB نیازی به تعریف جدول و روابط و ردیف های آن نیست. به همین علت می توان از آن برای Read Model استفاده کرد، چرا که هم پرفورمنس بهتری موقع کوئری گرفتن داده دارد و هم نیازی به تعریف روابط و عمل Join میان جداول مختلف نیست و میتوانیم داده را به صورت DeNormalized در آن ذخیره کنیم.نصب و راه اندازی MongoDB در ویندوزنصب Mongo روی ویندوز کار سر راست و ساده ای است. ابتدا به این لینک مراجعه کرده و نسخه Community آن را دانلود میکنیم. پس از دانلود و نصب آن Mongo Compass هم همراه با آن نصب میشود. Mongo Compass یک GUI برای مدیریت Mongo DB و داکیومنت ها و کالکشن های آن می باشد.برای آشنایی بیشتر با Mongo Compass می توانید به این لینک مراجعه کنید.پس از نصب، داشبورد Mongo Compass به طور خودکار بالا می آید. روی دکمه Connect کلیک کرده و هم اکنون میتوانیم از Mongo Compass استفاده کنیم.داشبورد کلی Mongo Compassبر روی دکمه Create Database کلیک کرده و یک دیتابیس درست میکنیم. نام هر دو Database Name و Collection Name را برای پروژه روی moviesdatabase قرار میدهیم و سپس بر روی دکمه Click Database کلیک میکنیم.آشنایی با معماری کلی پروژهپروژه دارای یک معماری استاندارد سه لایه (Data Access Layer-Core-Presentation) می باشد. در لایه Core جایی است که الگوی Mediator را به وسیله کتاب خانه MediatR پیاده سازی میکنیم و در لایه Data Access لایه برقراری با دیتابیس ها ( Mongo Db و SQL Server) می باشد. لایه Web جایی است که در آن API های پروژه قرار دارند و با استفاده از الگوی Dependecy Injection می توانیم از Handler های موجود در لایه Core استفاده کنیم.معماری استاندارد 3-tierنصب پکیج های مورد نیازبرای کار با Entity Framework Core نیاز به پکیج های متداول زیر داریم. این پکیج ها را در لایه Data Access نصب میکنیمInstall-Package Microsoft.EntityFrameworkCore -Version 5.0.3
Install-Package Microsoft.EntityFrameworkCore.Abstractions -Version 5.0.3
Install-Package Microsoft.EntityFrameworkCore.Design -Version 5.0.3
Install-Package Microsoft.EntityFrameworkCore.SqlServer -Version 5.0.3
Install-Package Microsoft.EntityFrameworkCore.Tools -Version 5.0.3آشنایی با Mongo Driver: یک کتابخانه مانند Entity Framework می باشد که از آن میتوانیم برای ارتباط با Mongo DB استفاده کنیم و به نوعی یک Abstraction روی دستورات مورد نیاز برای کوئری و ثبت داده روی Mongo DB به همراه دارد.( در این مقاله به طور عمیق به Mongo Driver نمی پردازیم و در حد ثبت داده و اجرای یک کوئری ساده به آن بسنده میکنیم. برای آشنایی بیشتر با Mongo Driver میتوانید به مستندات آن مراجعه کنید.)پکبج Mongo Driver را از طریق دستور زیر نصب میکنیمInstall-Package MongoDB.Driver -Version 2.11.6در لایه Core نیاز به دو پکیج زیر برای استفاده MediatR داریمInstall-Package MediatR -Version 9.0.0
Install-Package MediatR.Extensions.Microsoft.DependencyInjection -Version 9.0.0ساخت مدل های دیتابیسهمانطور که میدانید  در CQRS مدل های Read و Write از یکدیگر جدا هستند. به همین منظور برای یک موجودیت نیاز به ساخت دو مدل هستیم. برای پروژه دمو یک موجودیت به نام Movie در نظر گرفته شده است که برای مدل Read و Write آن به شکل زیر عمل میکنیم. یکی از مزایای جداسازی مدل Read و Write آن است که می توانیم در مدل Read دیتا را به صورت Denormalized در بیاوریم و آن را ذخیره کنیم که برای واکشی اطلاعات دیگر نیازی به Join نباشد.در مدل Read با صفت BsonId ابتدا Id دیتا را تعیین و مدل ذخیره سازی آن را تعیین میکنیم. سپس با صفت BsonElement تعیین میکنیم که فیلد به چه صورت در دیتابیس ذخیره شود. https://gist.github.com/babaktaremi/b46559f8694a802c486fddd5adc1e4e2 برای Write Model ها دو موجودیت Movie و Director را در نظر میگیریم که هر Director میتواند چند Movie داشته باشد. https://gist.github.com/babaktaremi/e73b96652028bcc75f604aa461a48097  https://gist.github.com/babaktaremi/c54d053afd34dd8d85d1353477a2a791 سپس به سراغ ساخت ApplicationDbContext می رویم و آن را به صورت زیر می سازیم.  https://gist.github.com/babaktaremi/839ab549214f396a9cdf5abd6f72f769 در کلاس Startup باید ApplicationDbContext ای که ساختیم را رجیستر کنیم https://gist.github.com/babaktaremi/9df59b7914604bf67ebdbcdac6b53edc پس از این کار، به Package Manger Console مراجعه کرده و اولین Migration را برای ساخت جداول اعمال میکنیم.ساخت ریپوزیتوری های مربوط به Write Modelپس از ساخت دیتابیس به سراغ ساخت Repository های مربوط به آن میرویم. در لایه Data Access  یک پوشه با نام WriteRepositories می سازیم و سپس در آن دو کلاس با نام های DirectorRepository و WriteMovieRepository می سازیم.در کلاس WriteMovieRepository سه متد با نام های AddMovieAsync ، GetMovieByIdAsync و DeleteMovie به صورت زیر خواهیم داشت https://gist.github.com/babaktaremi/8271295364b77b06d38acefdacfc6f28 برای DirectorRepository دو متد با نام های GetDirectorAsync و AddDirectorAsync به صورت زیر خواهیم داشت. https://gist.github.com/babaktaremi/cf72f126e8981cba22d7aabfd6c8f16d سپس این دو Repository را در Startup رجیستر میکنیم https://gist.github.com/babaktaremi/5caa4f7ced744373fd89b94c7fe9a080 ساخت ریپوزیتوری های مربوط به Read Modelدر لایه Data Access یک پوشه با نام ReadRepositories می سازیم و سپس در آن یک پوشه دیگر با نام Common می سازیم. در پوشه Common برای راحتی کار با Mongo Driver اقدام به ساخت یک BaseReadRepository می کنیم که در آن از متد های موجود در Mongo Driver استفاده میکنیمکلاس BaseReadRepository را به صورت زیر می سازیم. https://gist.github.com/babaktaremi/ae5d3722934d822a4f3cd1fcbd2a715b یکی از Best Practice ها در مورد Mongo Driver آن است که باید به صورت Singleton در سیستم رجیستر شود چرا که ساخت هرباره یک Instance از Mongo Client بسیار هزینه بر است. پس Mongo Database را به شکل زیر به صورت Singleton رجیستر میکنیم. دقت کنید که در اینجا Connection string و دیتابیس مربوطه در Mongo را هم به Mongo Client پاس میدهیم. یکی از مزیت های Mongo DB آن است که برای ساخت Collection ها نیازی به Migration نیست و به صورت خودکار ساخته می شوند. https://gist.github.com/babaktaremi/ec4b1e17e3cb8653832c14853dd6d145 سپس ReadMovieRespository را به شکل زیر می سازیم و آن را رجیستر میکنیم. https://gist.github.com/babaktaremi/c36ca2fe1c7d4d3d92b79eb5697d46e9  https://gist.github.com/babaktaremi/52f79f31e93f248508479466b18b4023 آشنایی با Background Service در ASP Net Coreدر ASP Net Core قابلیتی به نام Background Task وجود دارد که به کمک آن میتوان از یک Thread جداگانه در Background برای اجرای کارهای زمانبر و یا به صورت Interval بهره برد. هنگام استفاده از Background Task ها باید نکاتی را رعایت کنیم از جمله:برای مطالعه بیشتر درباره Background Service ها و همچنین Hosted Service ها میتوانید به این لینک مراجعه کنید.برای استفاده از Background Service ها نیازمند پکیج زیر هستیم که آن را در لایه Core نصب میکنیمInstall-Package Microsoft.Extensions.Hosting.Abstractions -Version 5.0.0مختصر توضیحی درباره الگوی Pub/Subدر الگوی Pub/Sub دو موجودیت داریم. Publisher و Subscriber که در آن Publisher یک Event را انتشار میدهد و Subscriber های آن Event بسته به شرایط یک سری عملیات خاص را مدیریت و اجرا میکنند. برای اینکه یک Subscriber از Event با خبر باشد، حتما باید عضوی از آن Event باشد که Publisher آن را Invoke میکنداین الگو برای ایجاد Loose Coupling و همچنین در Distributed System ها بسیار کاربرد دارد.آشنایی با channel ها در ASP Net Coreخیلی جاها نیاز هست که یک سری داده در یک صف نگهداری بشوند که بعدا بتوانیم توسط سرویسی آنها را پردازش کنیمدر مقیاس کوچک، به جای استفاده از Message Broker هایی مانند RabbitMq میتوانیم از Channel ها استفاده کنیم. دقت داشته باشید که چنل در مقایسه با Message Broker هایی مثل RabbitMq محدودیت هایی دارد مثلا از چنل ها فقط در یک Application میتوان استفاده کرد و قابلیت به اشتراک گذاشتن محتوای یک Channel میان چند Application وجود نداردچنل در واقع صفی هست که thread safe هست و میتواند یک یا چند prodcuer و consumer داشته باشد برای آشنایی بیشتر با Channel ها میتوانید به این لینک مراجعه کنیدساخت یک چنل جنریکبرای استفاده از Channel ها  در پروژه به سراغ ساخت یک Channel جنریک میرویم که بتوانیم از آن در چند جا استفاده کنیم. ابتدا در پروژه یک پوشه به نام Common ایجاد میکنیم و سپس در آن یک پوشه دیگر به نام BaseChannel ایجاد میکنیم. در این پوشه کلاس ChannelQueue را به شکل زیر ایجاد میکنیم.  https://gist.github.com/babaktaremi/d71d43d342d4b97330246c8b3652d0dd سپس این چنل جنریک را به صورت Singleton در سیستم رجیستر میکنیم https://gist.github.com/babaktaremi/2b6b3be51a69190e923f62e6fbc70f38 مسئله Eventual Consistencyزمانی که عملیات Write در دیتابیس اصلی اتفاق می افتد، این تغییر با یک تاخیر در دیتابیس های Read اعمال می شود.در این حالت چون تغییرات به صورت Eventually ( نه در لحظه، بلکه با تاخیر و در نهایت) Sync میشوند، ممکن است داده ای که در لحظه از دیتابیس Read واکشی می شود، به روز و آخرین نسخه نباشد.این تکنیک در سیستم های توزیع شده باعث می شود که در عوض یکپارچگی و ثبات داده ها (Consistency) به پرفورمنس بهتر، Scalability و  High Availability دست پیدا کنیمساخت اولین Commandابتدا به سرغ ساخت AddMovieCommand می رویم. این Command قرار است توسط Handler مربوطه، داده را هم در دیتابیس SQL Server و هم در Mongo DB ذخیره کند.در لایه Core یک پوشه به نام MovieApplication می سازیم و در آن یک پوشه دیگر به نام Commands می سازیم و در آن یک پوشه دیگر به نام AddMovie می سازیم. https://gist.github.com/babaktaremi/fb934879479d9552389c3e6eabaa3c8c  https://gist.github.com/babaktaremi/712a08964962bd2f4f5a602ac3621aec سپس به سراغ ساخت Command Handler مربوط به AddMovieCommand می رویم. https://gist.github.com/babaktaremi/570340969736a70326ee39ae71c13ba8 در خط 6 یک نمونه از چنل را Inject کرده ایم که قرار است Message این چنل توسط یک Background Service مدیریت شود و Movie توسط آن به دیتابیس Mongo اضافه شود. مدل Message قرار داده شده در چنل به شکل زیر است. ابتدا در پوشه MovieApplication یک پوشه با نام BackgroundWorker ایجاد میکنیم و سپس در آن یک پوشه دیگر با نام Common ایجاد میکنیم و در آن یک پوشه دیگر با نام Events ایجاد میکنیمدر این پوشه یک کلاس با نام MovieAdded به شکل زیر ایجاد میکنیم https://gist.github.com/babaktaremi/424c3df4ed471e4eff5a42dc107400c4 برای ساخت Background Worker مربوطه یک پوشه با نام AddReadMovie ایجاد میکنیم و در آن یک کلاس با نام AddReadModelWorker می سازیم. https://gist.github.com/babaktaremi/d0ccecce383cf8274866a5885ff0e199 چون Background Service ها به صورت Singleton در سیستم رجیستر میشوند، به کمک IServiceProvider یک Scope می سازیم و در آن سرویس های مورد نیاز را Inject میکنیم. در خط 19 و 20 به ترتیب WriteMovieRepository و ReadMovieRepository را از Scope ساخته شده دریافت میکنیم. سپس Movie را از دیتابیس دریافت کرده و در دیتابیس Mongo قرار میدهیم.در نهایت این Background Service را در Startup رجیستر میکنیم. https://gist.github.com/babaktaremi/af1680e21b254bff14532120e6731e22 به همین ترتیب میتوان سرویس های مربوط به Delete و Update را نیز نوشت.ساخت کوئری و دریافت Movie از Mongo DBدر پوشه MovieApplication یک پوشه دیگر با نام Queries می سازیم و در آن یک پوشه با نام GetMovieById می سازیم. در این پوشه دو کلاس با نام های GetMovieByNameQuery و GetMovieByNameQueryHandler را قرار میدهیم. https://gist.github.com/babaktaremi/50b4894e0033511829835f35062ed772 اگر یادتان باشد قبلتر در این مقاله در مورد ReadMovieRepository صحبت کردیم و در آن متدی با نام GetByNameAsync داشتیم که یک Movie را از دیتابیس Mongo بر اساس نام فیلتر و واکشی میکند. از این متد در Handler مربوط به این کوئری استفاده خواهیم کرد. https://gist.github.com/babaktaremi/981ecfa03014e4a17d8f3a6a16e13b2b ساخت کنترلر Movieابتدا یک Instance از IMediator را در کنترلر Inject میکنیم. سپس اکشن AddMovie را به شکل زیر می سازیم. https://gist.github.com/babaktaremi/7be3b102908fe5104d8133cc98d5e3a6 سپس بوسیله Swagger این اکشن را تست میکنیم.به SQL Server Management Studio مراجعه میکنیم. ملاحظه میشود که یک Instance از Movie و یک Instance از Director به دیتابیس اضافه شده است.حال به قسمت مهم ماجرا میرسیم. به سراغ Mongo DB Compass می رویم. در ابتدا یک دیتابیس با نام moviesdatabase ساختیم. به سراغ آن میرویم. مشاهده می شود که یک Collection با نام Movie ساخته شده است که در آن اطلاعات Movie به صورت Json قرار دارد.ساخت اکشن GetMovieهمانند اکشن AddMovie این بار با استفاده از MediatR به سراغ واکشی اطلاعات یک Movie می رویم.  https://gist.github.com/babaktaremi/0969747336d50c553ad008e569160a63 برای تست این اکشن به سراغ Swagger می رویم. ملاحظه میشود که اطلاعات از دیتابیس Mongo DB خوانده میشود چرا که یک ObjectId که Mongo به Document اختصاص میدهد نیز واکشی شده است.نتیجه گیریدر این مقاله یک پروژه بسیار ساده را با استفاده از MediatR و دیتابیس های MongoDB و SQL Server با پترن CQRS پیاده سازی کردیم. توصیه میکنم که این پروژه را از گیت هاب دریافت کرده و با همین پترن، اکشن Update یک Movie را پیاده سازی کنید. از طریق لینک زیر میتوانید پروژه را از گیت هاب دریافت کنید و اگر سوال و یا پیشنهادی دارید، خوشحال میشوم که آن را در قسمت نظرات مطرح کنید. https://github.com/babaktaremi/Sample-CQRS-Project مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Fri, 19 Feb 2021 18:01:11 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش gRPC در ASP.NET Core - قسمت دوم</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-grpc-%D8%AF%D8%B1-aspnet-core-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-rtafqie0sxa0</link>
                <description>آموزش gRPC در ASP.NET Core - قسمت دوم https://vrgl.ir/U1mV1 در این قسمت میخواهیم با تعریف message و service و rpc method آشنا بشیم، همچنین انواع Data Type ها در Protocol Buffer یاد میگیریم.و فایل های ایجاد شده توسط Protobuf Compiler را به صورت کامل بررسی میکنیم.تعریف و استفاده از enum ها را هم یاد میگیریم.به طور کلی 2 نوع رویکرد (approach) برای ساخت و طراحی API وجود دارد.Code FirstDesign First (Contract First)فریمورک gRPC  برای ساخت API های RPC از رویکرد دوم یا همان contract-first استفاده میکند، این رویکرد نسبت به code-first جدیدتر، بهتر، پایدارتر و تمیزتر است.همانطور که در قسمت اول گفته شد:در ابتدا باید برای هر سرویس gRPC یک قرارداد ایجاد کنیم، این قرارداد در یک فایل با فرمت proto. قرار میگیرد. از این قرارداد (contract) میتوان به چند شکل مختلف استفاده کرد:به شکل مستند (document): فایل های proto. دارای syntax آسان و به زبان انسان نزدیک هستند، پس در نتیجه با یک نگاه به این فایل ها میتوان متوجه شد که یک سرویس gRPC چه متدهایی را ارائه میکند.به شکل Blue Print: فایل های proto. را میتوانیم با استفاده از Protobuf Compiler به زبان های برنامه نویسی مختلف Compile و از آن ها در برنامه خودمان استفاده کنیم.پس فایل های proto. در آن واحد هم Document و هم Blue Print هستند.این فایل ها را دقیقا مثل یک Interface سی شارپی فرض کنید.به طور معمول یک فایل proto:دارای چند Message است، که به عنوان ورودی و خروجی متد ها قرار می گیرند.دارای یک Service است، که امضاء متد های RPC در آن قرار می گیرند.در ابتدا یک پروژه ASP.NET Core میسازیم.بعد از اینکه پروژه ساخته شد، باید در آن پکیج Grpc.AspNetCore را نصب کنیم.Install-Package Grpc.AspNetCore -Version 2.35.0 /* Package Manager Console */
dotnet add package Grpc.AspNetCore --version 2.35.0 /* .NET CLI  */سپس یک Folder به نام Protos در پروژه ایجاد کنید و در آن یک Protocol Buffer File به نام ticket_api.proto بسازید. (نام فایل حتما به صورت lower_snake_case باشد.)در این مرحله که فایل ticket_api.proto ساخته شد، در آن 2 خط کد مشاهده میکنیم.خط اول مربوط به ورژن Protocol Buffers است و حتما باید مقدارش مساوی با proto3 باشد.خط دوم به ما اجازه میدهد namespace کلاس هایی که بعدا از روی این فایل ticket_api.proto ساخته میشوند را به صورت صریح مشخص کنیم. https://gist.github.com/ArminShoeibi/56fa55dade70906fedf7d38c8f497ab1 در فایل های proto. ای که قرار است برای زبان #C کامپایل شوند میتوانیم دو option قرار دهیم.option csharp_namespaceoption optimize_for https://gist.github.com/ArminShoeibi/1548f9f96b19f22de328ca09cb84dd0b پیام ( Message ) چیست ؟پیام را دقیقا مثل یک DTO فرض کنید، وقتی یک message تعریف میکنیم، در اخر توسط Protobuf Compiler تبدیل به یک کلاس معمولی #C می شود.Message ها در ورودی و خروجی متدهای RPC استفاده می شوند، در واقع داده ها را بین Client و Server جا به جا میکنند.پیام (message) فقط می تواند دارای فیلد باشد و از خود هیچ رفتاری ندارد.نحوه تعریف Message نوشتن کلمه کلیدی messageتخصیص یک نام (Identifier) به آن (به صورت PascalCase نوشته شود.)نوشتن پراپرتی (فیلد) های مورد نیاز و تخصیص شماره به آن ها (نام فیلد ها به صورت lower_snake_case نوشته شود.) https://gist.github.com/ArminShoeibi/ea5fdf68478cbf2223cbb20cd9d71690 شماره فیلد (Field Number) چیست ؟شماره فیلد یکی از مهم ترین بخش های Protocol Buffer است، تخصیص شماره به فیلد های یک message اجباری است.وقتی که داده هایمان توسط Protocol Buffer به آرایه ای از byte ها(binary format) سریالایز میشوند، از این اعداد برای شناسایی فیلد ها استفاده میشود.در JSON  اشیا به صورت Key-Value Pair هستند و Key ها هم به صورت string هستند، ولی در proto به جای استفاده از string به عنوان Key از اعداد استفاده میشود به دلیل حجم کمتر.شماره فیلد های 1 تا 15 حجمشان یک byte است، در هر message از شماره 1 تا 15 را باید  به فیلد های پر مصرف و پر کاربرد اختصاص بدهیم. برای مثال در یک message بیست (20) فیلد داریم، بهتر است که حتما شماره 1 تا 15 را به فیلدهایی اختصاص بدهیم که بیشتر در Client و Server استفاده میشوند، برای مثال 5 فیلد از آن 20 فیلد مربوط به Audit هستند و خیلی کم استفاده میشوند یا اصلا استفاده نمیشوند ولی در message وجود دارند، در این صورت نباید شماره 1 تا 15 را به آن فیلد های Audit اختصاص بدهیم.چون شماره فیلد های 1 تا 15 حجمشان یک byte است بهتر است حتما فیلد های پر استفاده از این اعداد استفاده کنند. شماره فیلدهای 16 تا 2047  حجم شان دو byte است. از عدد های بالاتر هم می توانید استفاده کنید ولی پیشنهاد میشود اینکار را نکنید.انواع Data Type های Value Type در Messageبه طور پیشفرض پانزده  Data Type از نوع Value Type در فایل proto. وجود دارد و ما میتوانیم از آن ها برای فیلد هایمان استفاده کنیم.در تصویر پایین مشاهده میکنید که 15 عدد Data Type از نوع Value Type در یک فایل proto. قابل استفاده است، و این 15 عدد در آخر به 9 عدد Data Type در سی شارپ تبدیل میشوند.در کد و تصویر  زیر همانطور که مشاهده میکنید، از هر پانزده Data Type موجود در یک message استفاده کردیم. https://gist.github.com/ArminShoeibi/0204502b76cc3ec2fcdd34c6cd92c035 &quot;محمد جواد ابراهیمی&quot;:در اینجا چند نکته درباره نوع داده های protobuf حائز اهمیت هست:1️⃣ نوع های unsigned که تکلیفشون معلومه، فقط اعداد مثبت رو قبول میکنن مثلا نوع uint32 به uint درسی شارپ ترجمه میشه و فقط میتونه اعداد مثبت رو داشته باشه.2️⃣ نوع int32 به int در سی شارپ ترجمه میشه و میتونه هم اعداد منفی و هم مثبت رو داشته باشه ولی برای اعداد منفی غیر بهینه عمل میکنه چرا که اعداد منفی رو به 10 بایت انکود میکنه که مشابه یک عدد مثبت خیلی بزرگ رفتار میشه باهاش.3️⃣ نوع sint32 به int در سی شارپ ترجمه میشه و میتونه هم اعداد منفی و هم مثبت رو داشته باشه ولی برای اعداد منفی خیلی بهتر عمل میکنه چرا که از الگوریتم ZigZag استفاده میکنه و اعداد منفی رو به اعداد مثبت زیگ زاگی انکود میکنه به این صورت که عدد 1- میشه 1؛ عدد 1 میشه 2؛ عدد 2- میشه 3؛ عدد 3+ میشه 41-  ---&gt;  11+  ---&gt;  22-  ---&gt;  32+  ---&gt;  4...2147483647+  ---&gt;  42949672942147483648-  ---&gt;  4294967295در نتیجه برای اعداد منفی فضا و پردازش کمتری رو میطلبه ولی برای اعداد مثبت فضا و پردازش بیشتری رو میطلبه? نتیجه گیری:✅ از uint32  استفاده کنید اگر اعداد تون  &quot;فقط مثبت&quot; هستند.✅ از sint32 استفاده کنید اگر به مثبت و منفی نیاز دارید ولی اعداد تون &quot;معمولا منفی&quot; هستند.✅ از int32 استفاده کنید اگر به مثبت و منفی نیاز دارید ولی اعداد تون &quot;به ندرت منفی&quot; هستند.?نکته:✅ همین قضیه برای int64 و sint64 و uint64 هم صدق میکنه.✅ نوع های fixed هم طولشون ثابت هست (بر خلاف نوع های غیر fixed که طولشون متغیر varint هست) مثلا نوع fixed32 و sfixed32 همیشه 4 بایت اشغال میکنند و نوع های fixed64 و sfixed64 هم همیشه 8 بایت.سرویس (Service) چیست ؟سرویس (service) را دقیقا مثل یک Interface سی شارپی فرض کنید که فقط در آن امضای Method ها را قرار می دهیم، و فقط هم محل قرارگیری  امضای Method هایمان است.به متد هایی که در سرویس قرار میگیرند RPC Method می گویند.نحوه تعریف سرویس (service)نوشتن کلمه کلیدی serviceتخصیص یک نام به آن (به صورت PascalCase نوشته شود.)نوشتن متدهای مورد نیاز (همراه با پیشوند rpc) https://gist.github.com/ArminShoeibi/d9892d24254396f1c703057415204043 حالا که message  و service و rpc method های مورد نیاز را ساختیم، باید این فایل proto. را به صورت Server ای Compile کنیم.? نکته: یک فایل proto. را میتوان به 2 صورت Compile کرد.1. کامپایل مخصوص Client2. کامپایل مخصوص Serverکامپایل فایل ticket_api.proto به صورت Server و بررسی فایل های ایجاد شده توسط protoc در پروژه Serverابتدا وارد Connected Services پروژه GrpcServer می شویم.سپس بر روی دکمه Add کلیک میکنیم.و در مرحله بعدی گزینه gRPC را انتخاب میکنیم.سپس بر روی دکمه Browse کلیک میکنیم و فایل ticket_api.proto را در FileDialog انتخاب میکنیم.و بعد نوع کلاس هایی که قرار است توسط protoc ایجاد شوند را بر روی Server قرار میدهیم.در مرحله بعدی بر روی دکمه Close کلیک میکنیم و سپس پروژه را Save میکنیم.بعد از اینکه عمل Save را انجام دادید باید پروژه مورد نظر یا سولوشن را Build کنید.در مراحل بالا دقیقا چه کاری کردیم ؟در تصاویر بالا فایل ticket_api.proto را به صورت Server برای پروژه مان Compile کردیم و در این حین در پشت صحنه اتفاق هایی افتاد.مقدار Build Action فایل ticket_api.proto به Protobuf Compiler تغییر کرد.کلاس هایی از روی این فایل مخصوص به Grpc Server تولید شد.حالا بعد از هر بار تغییر فایل ticket_api.proto و Build کردن پروژه، کلاس های تولید شده آپدیت و ویرایش میشوند.در این مرحله میخواهیم فایل ها و کلاس هایی که توسط کامپایل شدن فایل ticket_api.proto ساخته شدند را بررسی کنیم.در ابتدا بر روی پروژه کلیک چپ کنید و سپس گزینه Show All Files را Enable کنید.سپس در Solution Explorer وارد مسیر obj/Debug/net5.0/Protos شوید.مشاهده میکنید که 2 فایل در آن با فرمت cs. وجود دارد.فایل اول بدون پسوند Grpc که محل قرارگیری message ها است، message ها توسط protoc به کلاس های معمولی سی شارپ تبدیل میشوند.فایل دوم با پسوند Grpc که محل قرارگیری service است و متد CreateTicket در آن قرار گرفته است.نام این 2 فایل از روی نام ticket_api.proto برداشته شده است، Protobuf Compiler این نام را از فرمت lower_snake_case به PascalCase تغییر داده و از آن برای این 2 فایل سی شارپی استفاده کرده است.وقتی که یک فایل proto. مخصوص به Server کامپایل میشود، متدهای قرار گرفته در service را باید حتما override و سپس Implement کنیم.این متد ها در یک کلاسی به نام ServiceName+Base قرار گرفته اند، دقیقا protoc نام Service را بر میدارد و یک پسوند Base به آن اضافه میکند.در اینجا سرویسی که در فایل ticket_api.proto قرار داده بودیم نامش TicketService بود و الان باید توقع این را داشته باشیم که protoc یک کلاس به نام TicketServiceBase برای ما ایجاد کرده باشد. پس همیشه در سمت سرور باید کلاسی که نامش از نام سرویس + Base تشکیل شده است را استفاده کنیم.تا الان چه کارهایی انجام دادیم:ساخت پروژه ASP.NET Coreنصب پکیج Grpc.AspNetCoreایجاد فایل ticket_api.proto تعریف message و serviceکامپایل فایل ticket_api.proto به صورت Serverکامپایل فایل ticket_api.proto به صورت Client و بررسی فایل های ایجاد شده توسط protoc در پروژه Clientابتدا در همان Solution یک پروژه Console میسازیم.به این پروژه پسوند GrpcClient را میدهیم.سپس باید در پروژه کنسولی که به عنوان GrpcClient ساختیم، 3 عدد پکیج نصب کنیم.Install-Package Grpc.Tools -Version 2.35.0NuGet Gallery | Grpc.Net.ClientFactory 2.35.0NuGet Gallery | Google.Protobuf 3.14.0Install-Package Grpc.Tools -Version 2.35.0
Install-Package Google.Protobuf -Version 3.14.0
Install-Package Grpc.Net.ClientFactory -Version 2.35.0در این مرحله باید فایل ticket_api.proto را هم در این پروژه به صورت Client کامپایل کنیم، دقیقا طبق همان مراحل بالا از بخش Connected Service این فایل را کامپایل میکنیم.وقتی که مراحل بالا انجام شد، یک Link از فایل ticket_api.proto که در پروژه GrpcServer قرار گرفته بود به پروژه GrpcClient ایجاد میشود.می توانید این Link را در فایل csproj پروژه GrpcClient مشاهده کنید.راه دیگری هم وجود دارد که می توانیم در ابتدا یک کپی از فایل ticket_api.proto بگیریم و سپس یک Folder به نام Protos در پروژه GrpcClient بسازیم و سپس آن فایل را paste کنیم، و بعد از روی آن فایل مراحل Connected Service را انجام دهیم.خب بعد از اینکه مراحل Connected Service تمام شد، و یک link از فایل ticket_api.proto در پروژه GrpcClient ایجاد شد، سپس باید فایل های ایجاد شده توسط protoc را بررسی کنیم.اول Show All Files را برای پروژه GrpcClient را Enable کنید.سپس همانطور که مشاهده میکنید، همان 2 فایلی که در پروژه Server ایجاد شدند، در این پروژه هم ایجاد شدند دقیقا با همان نام و فرمت.فایل اول دارای Message ها است.فایل دوم دارای service  است.فایل دوم یعنی TicketApiGrpc دارای یک کلاس است که نامش از نام سرویس + Client تشکیل میشود. یعنی service ما که در فایل ticket_api.proto قرار گرفته بود نامش TicketService بود، الان باید توقع داشته باشیم که کلاسی به نام TicketServiceClient را برای ما ایجاد شده باشد.وظیفه Client این است که یک نمونه از کلاس TicketServiceClient بسازد و متدهای موجود در آن را فراخوانی کند و از آن ها استفاده کند.خب کل فرق کامپایل به صورت Client و به صورت Server رو میتونید در تصویر زیر مشاهده کنید.بررسی Enumeration در Protobufدر فایل proto. می توانیم Enum های دلخواه خودمان را تعریف کنیم و از آن ها به عنوان یک Data Type برای فیلد هایمان استفاده کنیم.نحوه تعریف Enumنوشتن کلمه کلیدی enumتخصیص نام به enum صورت PascalCaseتعریف constant ها به صورت UPPERCASE و تخصیص عدد به آن هانکته: Enum را می توانیم در 2 جا تعریف کنیم.1. در داخل message2. در بیرون messageاگر یک enum فقط قرار است در داخل یک message استفاده شود، پس آن را در داخل همان message تعریف می کنیم.ولی اگر قرار است در چند message از آن استفاده بشود آن را در بیرون message ها تعریف میکنیم.در کد و تصویر زیر مشاهده می کنید که TicketStatus را بیرون از message ها تعریف کردیم و از آن در داخل TicketCreationResultDto استفاده کردیم.در تصویر و کد زیر مشاهده میکنید که TicketStatus را در داخل message تعریف کردیم.همانطور که در زبان #C می توانیم  در هر enum چند فیلد با نام های مختلف و مقدار مساوی داشته باشیم، می توانیم همین رفتار را  هم در enum های Protobuf  داشته باشیم.ولی باید این ویژگی را در enum های Protobuf به صورت صریح فعال کنیم.سورس کد پروژه: https://github.com/ArminShoeibi/DNZ.TicketingSystem مقالات بیشتر در کانال دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>آرمین شعیبی</author>
                <pubDate>Tue, 09 Feb 2021 22:43:04 +0330</pubDate>
            </item>
                    <item>
                <title>بررسی عملی CQRS- بخش دوم: بررسی الگوی Mediator با استفاده از کتابخانه MediatR</title>
                <link>https://virgool.io/dotnetzoom/%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%B9%D9%85%D9%84%DB%8C-cqrs-%D8%A8%D8%AE%D8%B4-%D8%AF%D9%88%D9%85-%D8%A8%D8%B1%D8%B1%D8%B3%DB%8C-%D8%A7%D9%84%DA%AF%D9%88%DB%8C-mediator-%D8%A8%D8%A7-%D8%A7%D8%B3%D8%AA%D9%81%D8%A7%D8%AF%D9%87-%D8%A7%D8%B2-%DA%A9%D8%AA%D8%A7%D8%A8%D8%AE%D8%A7%D9%86%D9%87-mediatr-aavpwkagy2pu</link>
                <description>در این بخش به یکی از ملزومات پیاده سازی CQRS یعنی الگوی Mediator و کتابخانه MediatR می پردازیم. https://vrgl.ir/01pUA در قسمت قبلی به طور اجمالی با CQRS آشنا شدیم و اشاره کردیم که الگوی Mediator به ارائه یک سولوشن تمیز در ارتباط با CQRS کمک شایانی میکند و باعث ایجاد Loose Coupling می شود. در این قسمت به بررسی کتابخانه معروف و محبوب MediatR از جیمی بوگارد می پردازیم.در این مقاله از Gist استفاده شده است و لود شدن بخش مربوط به کد ها ممکن است کمی زمانبر باشدالگوی Mediatorدر الگوی Mediator دیگر کلاس ها و سرویس ها با یکدیگر مستقیم در ارتباط نیستند بلکه درخواست خود را از طریق یک واسط می فرستند و از طریق همان واسط، پاسخ مربوطه را دریافت میکنند. این کار باعث می شود که وابستگی شدید بین دو سرویس به حداقل برسد و Loose Coupling داشته باشیم که به توسعه و نگهداری یک پروژه کمک شایانی میکند.کتابخانه MediatRکتابخانه MediatR یک پیاده سازی از الگوی Mediator در دات نت می باشد که از Request و Command پشتیبانی میکند. در MediatR هر درخواست در یک صف در حافظه قرار میگیرد و سپس به Handler مربوط به آن درخواست وصل می شود. MediatR هیچ وابستگی به سایر کتابخانه ها ندارد و از Generic ها در سی شارپ برای ساخت Response و همچنین تعیین Handler ها استفاده میکند. یکی دیگر از ویژگی های بسیار خوب MediatR، پشتیبانی از الگوی Pub/Sub می باشد که به وسیله آن میتوان یک notification ایجاد کرد و در جاهای مختلف پروژه این notification را صدا زد.الگوی Pub/Sub با استفاده از Notification ها در MediatRبررسی Request و Request Handler در MediatRدر MediatR برای رسیدگی به Request ها از Handler ها استفاده میکنیم. هر درخواست از اینترفیس IRequest (و یا مدل Generic آن ) ارث بری میکند. و Handler مربوط به آن از اینترفیس IRequestHandler&lt;TRequest,TResponse ارث بری میکند. که TRequest همان درخواست می باشد که از IRequest ارث بری کرده است و به این ترتیب Request به Handler خود مپ میشود. در نمونه کد زیر یک Request و Request Handler مربوط به آن را میتوانید ببینید. https://gist.github.com/babaktaremi/61bae27d61130fb1255fb7c17c2dd048  https://gist.github.com/babaktaremi/8cf76b9b457cae9f332517f4de1a525a در مدل جنریک Handler، خروجی درخواست معلوم میشود که از جنس boolean می باشد. اینترفیس IRequestHandler یک متد به اسم Handle دارد که باید پیاده سازی شود.سپس در کنترلر مربوطه، یک instance از IMediator را تزریق کرده و کلاس Request را به شکل زیر به آن پاس میدهیم. https://gist.github.com/babaktaremi/23883059e79e13c8d14fef42f0c3620c استفاده از MediatR در پروژه عملیپس از ایجاد یک پروژه نمونه، پکیج مربوط به MediatR را از طریق Package Manager Console با دستور زیر نصب میکنیم:Install-Package MediatRو یا از طریق Manage Nugget Packages پکیج مربوط به MediatR را نصب میکنیم:سپس نیاز است برای استفاده از MediatR در Dependency Injection پکیج MediatR.Extensions.Microsoft.DepenedencyInjection را نیز نصب کنیم. این پکیج اینترفیس Imediator و همچنین Pipeline ها و Handler و Request ها را به صورت Transient به سیستم تزریق میکند.برای نصب میتوانیم از طریق Package Manager Console دستور زیر را اجرا نماییم:Install-Package MediatR.Extensions.Microsoft.DependencyInjection و یا از طریق Manage Nugget Manager برای نصب اقدام نماییمسپس در Startup.cs در قسمت Configure Services سرویس مربوط به MediatR را تزریق میکنیم. https://gist.github.com/babaktaremi/d1bdee85964361c5a656ecee734280b1 به AddMediaR باید Assembly مربوط به قسمتی که Handler ها در آن قرار دارد را پاس بدهیم. در این پروژه چون Class Library جدا نداریم همان Assembly مربوط به Startup را پاس دادیم. تمامی تنظیمات مربوط به MediatR انجام شده است. ایجاد اولین Request و Handlerبرروی سولوشن راست کلیک کرده یک فولدر به نام Data ایجاد میکنیم. در این پوشه، پوشه دیگری به نام Model ایجاد میکنیم که در این پوشه قرار است مدل ها و انتیتی های پروژه قرار بگیرند.در پوشه Model یک کلاس به نام Book ایجاد میکنیمسپس در پوشه Model یک کلاس به نام Book ایجاد میکنیم که شامل Property های زیر می باشد. https://gist.github.com/babaktaremi/0c2e6b9e6086392ee0edfb3d80d24fa0 این کلاس قرار است نقش Book Entity را بازی کند.در پوشه Data، یک پوشه دیگر تحت عنوان   Repositories می سازیم.سپس در پوشه Repository یک پوشه دیگر تحت عنوان BookRepository می سازیم که قرار  است در این پوشه ریپوزیتوری مربوط به کلاس Book را بسازیم. در این پروژه از In Memory Database استفاده میکنیم و لیستی از Book را درون حافظه ذخیره میکنیم و از آن نیز Book را باز خوانی میکنیم.یک اینترفیس با نام IBookRepository می سازیم. https://gist.github.com/babaktaremi/6aca444a32be029d3fbecfc1e63d27e1 سپس کلاسی با نام BookMockRepository می سازیم و این اینترفیس را در آن به شکل زیر پیاده سازی میکنیم. https://gist.github.com/babaktaremi/3b7e83db74e035aca81f779d527ba332 سپس این اینترفیس را به صورت Scoped رجیستر میکنیم.حال همه چیز برای ایجاد اولین Handler آماده است. در پروژه یک پوشه دیگر به نام Application ایجاد میکنیم. لایه Application وظیفه سازماندهی به ریکوئست ها را بر عهده دارد ( Request Orchestration). برای مطالعه بیشتر درباره لایه اپلیکیشن و همچنین لایه بندی پروژه میتوانید به این لینک مراجعه کنید.در پوشه Application یک پوشه به نام BookApplication ایجاد میکنیم. در این پوشه یک پوشه دیگر به نام GetBookById ایجاد میکنیم.در این پوشه دو کلاس قرار میگیرد:GetBookByIdRequestGetBookByRequestHandlerکلاس GetBookByIdRequest از اینترفیس IRequest ارث بری میکند.این اینترفیس جنریک یک پارامتر ورودی دارد که نوع Response یک Request را مشخص میکند. که در اینجا Response درخواست GetBookByIdRequest یک Book می باشد. https://gist.github.com/babaktaremi/2fecbc1dea3c97a07cd5b206973d6595 در قدم بعدی برای این Request یک Handler مینویسیم که وظیفه ایجاد Response  برای GetBookByIdRequest را برعهده دارد. هر Handler از اینترفیس IRequestHandler ارث بری میکند. این اینترفیس دو پارامتر را به عنوان وروردی جنریک میگیرد، اولین پارامتر Request و دومین پارامتر  Response مربوطه می باشد. این اینترفیس یک متد به نام Handle از جنس Task دارد که باید پیاده سازی شود.در این Handler یک نمونه از IBookRepository را Inject میکنیم. در متد Handle با استفاده از BookRepository یک Instance از Book را به عنوان Response بر میگردانیم. https://gist.github.com/babaktaremi/28f5cd4f1424097a0f0e908aa235dc15 سپس یک کنترلر برای استفاده از این Handler می سازیم و نام آن را BookController قرار میدهیم. برای استفاده از Handler های MediatR ، اینترفیس IMeditor را در کنترلر تزریق میکنیم.برای دریافت Book از طریق Id، ریکوئست GetBookByIdRequest را به متد Send در اینترفیس IMediator پاس میدهیم.حال این متد یک Book را به ما بر میگرداند چرا که در تایپ IRequest یک Book را به عنوان Response تعریف کرده ایم. https://gist.github.com/babaktaremi/41ab0e9c5f2ff30820d1033416f62eb6 به وسیله Swagger این API را چک میکنیم. و جواب یک Instance از Book می باشد. آشنایی با Behavior های MediatRبا استفاده از Behavior ها در MediatR میتوان پایپ لاین های مخصوص ساخت که در حین، قبل و یا بعد اجرای یک Handler اجرا شوند.میخواهیم با استفاده از Behavior ، درخواست هایی که به سمت هرکدام از Handler ها فرستاده میشوند را چک کنیم و در صورت وجود خطا،آن را لاگ کنیم.در پوشه Application یک پوشه دیگر به نام Common می سازیم. در این پوشه یک کلاس به نا LoggingBehavior ایجاد میکنیم. این کلاس را به صورت جنریک در می آوریم. برای ساخت پایپ لاین در MediatR باید کلاس، اینترفیس IPiplelineBehavior ارث بری کند. این اینترفیس یک متد به نام Handle و از جنس Task دارد که باید پیاده سازی شود. https://gist.github.com/babaktaremi/7733bf688e8aadda7181064199b495a0 در قطعه کد بالا، یک اینستنس از ILogger را به پایپ لاین تزریق کردیم که در صورت وقوع خطا از آن برای لاگ کردن خطا استفاه میکنیم. برای اینکه در ادامه Handler مربوطه صدا زده شود،نیاز است که next صدا زده شود. این عمل در اصل باعث تشکیل Pipleline میشود و باعث میشود که LoggingBehavior در مسیر یک درخواست برای رسیدن به یک Handler قرار بگیرد.پس از ساخت Pipeline آن را در Startup رجیستر میکنیم. https://gist.github.com/babaktaremi/774775084b7187adfaf9b8d6ef4de393 برای تست پایپ لاین، در GetBookByIdRequestHandler یک خطای تستی پرتاب میکنیم.و مجدد Api ای که ساختیم را در Swagger صدا میزنیم. مشاهده میکنیم و که Pipeline ساخته شده حین درخواست، خطا را Catch و آن را لاگ کرده است و API در واقع خطا را دریافت نکرده استدر MediatR دو Behavior به صورت پیش فرض وجود دارد مه به صورت پیش فرض در MediatR رجیستر شدند و میتوان آنها را پیاده سازی و استفاده کرد.IRequestPreProccessorاین پایپ لاین قبل از اجرای هر Handler اجرا میشود. این اینترفیس یک متد به نام Process دارد که نوع درخواست را در را میتوان به وسیله آن در اختیار داشت و با توجه به آن قبل از هر درخواست یک سری عملیات را انجام داد. https://gist.github.com/babaktaremi/a8d52d5e8f0ed8f713bc4caaca37ad13 IRequestPostProcessorاین پایپ لاین بعد از اجرای هر Handler اجرا میشود. این اینترفیس یک متد به نام Process دارد که به وسیله آن میتوان نوع درخواست و همچنین نوع Response را در اختیار داشت و با توجه به آنها یک سری عملیات انجام داد. https://gist.github.com/babaktaremi/13e98c01eb53b578b0a7a99989327879 پیاده سازی الگوی Unit of Work توسط Post Processor در MediatRهمانطور که میدانید، Post Processor ها بعد از اجرای هر Handler اجرا میشوند و توسط آنها میتوان به نوع درخواست و همچنین نوع Response دسترسی داشت. در این قسمت توسط یک Post Processor میخواهیم الگوی Unit of Work را پیاده سازی کنیم. برای مطالعه درباره Unit of Work میتوانید به این لینک مراجعه کنیددر پوشه Common یک اینترفیس خالی با نام ICommitable ایجاد میکنیم. بوسیله این اینترفیس میخواهیم تمامی درخواست هایی که قرار است در دیتابیس تغییرات داشته باشند را علامتگذاری کنیم. https://gist.github.com/babaktaremi/fc3c09494801509af5834846ac2489b9 در پوشه BookApplication، یک پوشه جدید به نام AddBook ایجاد میکنیم. در این پوشه یک کلاس به نام AddBookRequest به شکل زیر ایجاد میکنیم. این کلاس از اینترفیس ICommitable ارث بری میکند و به نوعی آن را با اینترفیس ICommitable علامت گذاری میکنیم. https://gist.github.com/babaktaremi/c084475ce6485efa18738d015f98ffac هندلر مربوط به این Request را به شکل زیر میسازیم.دقت کنید که برای اضافه کردن مدل جدید، از Mock Repository که قبلتر ساختیم استفاده میکنیم. https://gist.github.com/babaktaremi/396b01cb184703d7f55121720977bf4b برای پیاده سازی الگوی Unit of Work به سراغ پیاده سازی Post Processor آن میرویم. در پوشه Common یک کلاس به نام CommitPostProcessor می سازیم. در اینجا برای نمونه در کنسول لاگ ثبت میکنیم و به نوعی ثبت داده در دیتابیس را شبیه سازی میکنیم. https://gist.github.com/babaktaremi/f561528ebc17253afc303b85c9816332 همانطور که میدانید Post Processor ها در انتهای هر Request به MediatR اجرا میشوند، پس در اینجا در انتهای هر Request چک میکنیم که آیا Request با ICommitable علامت گذاری شده است یا خیر و اگر علامت گذاری شده بود تغییرات را در دیتابیس اعمال میکنیم. به طور مثال اگر از EF Core استفاده میکنید میتوانید در اینجا متد Save Changes روی DbContext را صدا بزنید.(برای مشاهده منبع اصلی این قسمت میتوانید به این لینک در گیت هاب مراجعه کنید.)در نهایت API Action مربوطه را ساخته و با استفاده از اینترفیس IMediator این درخواست را صدا میزنیم. https://gist.github.com/babaktaremi/b73f23b195c1b1e68b7fbff3936ebf77 بررسی الگوی Pub/Subدر الگوی  Pub/Sub دو موجودیت داریم. Publisher و Subscriber که در آن Publisher یک Event را انتشار میدهد و Subscriber های آن Event بسته به شرایط یک سری عملیات خاص را مدیریت و اجرا میکنند. برای اینکه یک Subscriber از Event با خبر باشد، حتما باید عضوی از آن Event باشد که Publisher آن را Invoke میکند این الگو برای ایجاد Loose Coupling و همچنین در Distributed System ها بسیار کاربرد دارد.برای مطالعه بیشتر در مورد پیاده سازی الگوی Pub/Sub در سی شارپ با استفاده از Event Handler ها میتوانید به این لینک مراجعه کنید.پیاده سازی الگوی Pub/Sub در MediatRپیاده سازی الگوی Pub/Sub در MediatR بسیار ساده است. در MediatR مفهومی به اسم Notification وجود دارد. یک کلاس از اینترفیس INotification ارث بری میکند. این کلاس نمایانگر یک Event در MediatR میباشد که بقیه Handler ها و یا کلاس ها میتوانند این کلاس را Publish کنند. سپس Handler مربوط به این Event ( که از INotificationHandler ارث بری کرده است) اقدامات مربوط به این Event را انجام میدهد.از Notification ها میتوان برای انجام کار هایی که بین چند Handler مشترک هستند استفاده کرد.ایجاد یک Notificationفرض کنید میخواهیم به ازای ایجاد هر Book یک Email به ادمین وب سایت ارسال کنیم. ابتدا در پوشه Common یک پوشه دیگر به نام Notifications ایجاد میکنیم.سپس در این پوشه یک پوشه دیگر به نام EmailNotification ایجاد میکنیم.در این پوشه دو کلاس به نام های EmailNotification و EmailNotificationHandler ایجاد میکنیم.کلاس EmailNotification را به شکل زیر ایجاد میکنیم. https://gist.github.com/babaktaremi/2241362c1c4c101473ad7f99ac648990 دقت کنید که این کلاس از INotification ارث بری میکند و شامل دو پراپرتی Email Address و              Email Content می باشد که باید مقدار دهی شوند.سپس کلاس EmailNotificationHandler را به شکل زیر پیاده سازی میکنیم. برای شبیه سازی، به جای ارسال ایمیل، یک لاگ در کنسول ثبت میکنیم. https://gist.github.com/babaktaremi/a98a84a029602da376117fd064fa2bc0 به BookController بر میگردیم. در اکشن CreateBook برای استفاده از این Notification از متد Publish اینترفیس IMediator استفاده میکنیم. https://gist.github.com/babaktaremi/dd1d39c1d4232f71fb158155b53262a7 در خط 8 کد بالا، Notification مربوط به ارسال Email را پابلیش کرده ایم. در کنسول خواهیم داشت:مشاهده میشود که Event مربوطه به ارسال Notification توسط EmailNotifcation Handler اجرا شده است.نتیجه گیریدر این قسمت به بررسی MediatR پرداختیم و با ویژگی های آن آشنا شدیم. پیاده سازی الگوی Mediator با استفاده از کتابخانه MeidatR کاری بسیار راحت و بی دردسر است که به ارائه سولوشن تمیز و ایجاد Loose Coupling (که از ملزومات پیاده سازی CQRS می باشد) کمک بسیازی میکند. همچنین در MediatR میتوان Pipeline های اختصاصی ایجاد کرد و با استفاده از Post Processor ها الگوی Unit of Work را پیاده سازی کرد. در قسمت بعد به پیاده سازی پروژه به روش CQRS با استفاده از MediatR می پردازیم.اگر به سورس کد این قسمت نیاز داشتید میتوانید آن را لینک زیر در گیت هاب دریافت نمایید. و همچنین اگر سوال یا مطلبی بود، خوشحال میشوم که آن را در بخش نظرات مطرح فرمایید. https://github.com/babaktaremi/MediatRExplore مقالات بیشتر در دات نت زومhttps://t.me/DotNetZoom </description>
                <category>دات نت زوم</category>
                <author>بابک طارمی</author>
                <pubDate>Tue, 26 Jan 2021 09:50:29 +0330</pubDate>
            </item>
                    <item>
                <title>آموزش gRPC در ASP.NET Core - قسمت اول</title>
                <link>https://virgool.io/dotnetzoom/%D8%A2%D9%85%D9%88%D8%B2%D8%B4-grpc-%D8%AF%D8%B1-aspnet-core-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-or6ti2wsqb33</link>
                <description>آموزش gRPC در ASP.NET Coreجی آر پی سی(gRPC) چیست ؟جی آر پی سی(gRPC) یک فریمورک رایگان، قدرتمند، مدرن، پر سرعت و متن بازِ RPC است.به طور کلی 3 نوع مدل برای طراحی API وجود دارد.SOAPRESTRPC (Remote procedure call)فریمورک gRPC به ما اجازه میدهد که API هایی از نوع RPC بسازیم.این  نوع API ها به Client ها اجازه میدهند که متدهای قرار گرفته در Server را در برنامه خودشان فراخوانی کنند به شکلی که انگار متدها واقعا در برنامه خودشان پیاده سازی شده است.مثال:در تصویر زیر برنامه Server دارای یک متد به نامGetOrder است.public Order GetOrder(int num);حالا gRPC چیکار میکنه دقیقا ؟ اجازه میده به Client ها که دقیقا این متد رو داخل برنامه خودشون فراخوانی کنن و از خروجی اش استفاده کنن.مکانیزم RPCمزایای فریمورک gRPCتسهیل ایجاد ارتباط بین Microservice ها.پر سرعت است.استفاده از HTTP/2 به عنوان Transfer Protocol.پشتیبانی از Client Streamingپشتیبانی از Server Streamingپشتیبانی از Bidirectional Streamingاز XML و JSON سریع تر، سبک تر، ساده تر است.به دلیل Serialize کردن داده ها به binary data format حجم Payload خیلی کمی دارد.امکان استفاده در بیشتر زبان های برنامه نویسی( Polyglot )پروتکل بافر (Protocol Buffer) چیست ؟پروتکل بافر یک سریالایزرِ Binary است.فریمورک gRPC  به صورت پیش فرض برای Serialize کردن  داده ها از Protcol Buffer استفاده میکند.پروتکل بافر داده ها را به آرایه ای یا استریمی از byte ها تبدیل میکند، و به همین دلیل سبک تر و سرعتش نسبت به JSON و XML بالاتر است.فایل  تنظیماتی proto. چیست ؟فریمورک gRPC برای ساخت API ها از رویکرد contract-first استفاده میکند.رویکرد contract-first چیست ؟در این روش،  ابتدا قبل از کد نوشتن باید متدهایی که قرار است در API مان ارائه بدهیم را به صورت کامل همراه با پارامتر هایشان را بنویسیم و سپس آن ها را پیاده سازی میکنیم.در فریمورک gRPC متد هایمان را باید در یک service قرار بدهیم، و پارامتر های آن ها را باید به صورت message تعریف کنیم، و سپس آن ها را در یک فایل با فرمت proto. قرار دهیم.message را دقیقا مثل DTO های خودمان تصور کنید.فایل proto محل قرارگیری متد ها و پارامتر هایی  است که قراره در API مان به سرویس گیرندگان ارائه کنیم.(  فایل ها proto را مثل یک قرارداد بین Client ها و Server ها تصور کنید ).در قسمت های بعدی به صورت کامل و عمیق با فایل های تنظیماتی proto آشنا می شویم.ساخت اولین پروژه با gRPC در ASP.NET Coreدر ادامه قصد داریم که یک پروژه خیلی ساده را با فریمورک gRPC و ASP.NET Core پیاده سازی کنیم.در این پروژه Server یک متد به نام Sum برای جمع دو عدد با نوع داده long به Client ارائه میکند و Client می تواند با فراخوانی این متد در برنامه خودش از پیاده سازی آن استفاده کند.در ابتدا یک پروژه ASP.NET Core به صورت Empty میسازیم. سپس باید پکیج Grpc.AspNetCore را نصب کنیم.Install-Package Grpc.AspNetCore -Version 2.34.0در مرحله بعدی طبق رویکرد contract-first اول باید متدها و پارامتر هایی که میخواهیم به بیرون ارائه کنیم را در یک فایل proto بنویسیم.یک فولدر به نام Protos میسازیم و در آن یک فایل proto به نام calculator اضافه میکنیم.سپس باید در فایل ساخته شده متد ها و پارامتر های آن ها را بنویسیم.همانطور که مشاهده میکنید متد Sum یک ورودی به نام SumRequest دارد و یک خروجی از نوع SumResponse https://gist.github.com/ArminShoeibi/0ed2d7fe6b12d29fbab752b30f66ca30 وقتی که قرارداد رو نوشتیم، باید آن را توسط Protobuf Compiler کامپایل کنیم، در قسمت های بعدی در مورد این کامپایلر صحبت خواهیم کرد.وارد Properties فایل calculator.proto می شویم.مقدار BuildAction را بر روی Protobuf Compiler قرار دهید.سپس مقدار gRPC Stub Classes را بر روی Server Only قرار دهید.سپس پروژه را Build کنید.( خیلی مهم) وقتی که مرحله بالا را انجام دهید، Protobuf Compiler از روی آن فایل calculator.proto برای شما دو عدد فایل سی شارپی با فرمت cs ایجاد میکند.(در مورد این 2 فایل در قسمت های بعدی به صورت کامل صحبت خواهیم کرد.) الان فقط برای پیاده سازی متد Sum در برنامه سرور باید از کلاس CalculatorServiceBase ارث بری کنیم، این کلاس در یک کلاس Static به نام CalculatorService قرار گرفته است.پس یک فولدر به نام GrpcServices  ایجاد کنید و در آن یک کلاس به نام CalculatorGrpcService بسازید و سپس از کلاس CalculatorServiceBase  ارث بری کنید.حالا باید متد Sum را پیاده سازی کنیم، Protobuf Compiler این متد را به صورت  virtual در کلاس CalculatorServiceBase قرار داده است و ما باید آن را override کنیم.بعد از اینکه متد Sum رو override کردیم باید منطق خودمون در اون پیاده سازی کنیم. https://gist.github.com/ArminShoeibi/61561512c7b6207f0c9ba0c7b6a595b2 حالا که متد Sum تکمیل شد باید سرویس gRPC رو به DI Container  پروژه مون اضافه کنیم.سپس میان افزار UseHttpsRedirection  را هم اضافه می کنیم.(خیلی مهم)و سپس کلاس CalculatorGrpcService رو هم در endpoint ها ثبت می کنیم. https://gist.github.com/ArminShoeibi/6980cb25e9951210fe56e5dd34057bf0 بعد از این مرحله باید اجرای پروژه مون رو به دست Kestrel بسپاریم، چون IIS هنوز از gRPC پشتیبانی نمیکنهنکته: اگر سیستم عامل تان ویندوز 10 هست و Build آن 20300.1000  و یا بالاتر است، همچنان میتوانید از IIS استفاده کنید و پروژه را با آن اجرا کنید.همچنین پروژه باید بر روی In-process hosting قرار گرفته باشد.طبق تصویر اجرای پروژه را به عهده Kestrel بزارید.خب الان پروژه سرور مان تکمیل شد و در اصل یک متد RPC به بیرون ارائه میکنه.الان وقت ساختن یک پروژه Client است که بتواند از این متد Sum استفاده کند، در همان سولوشن یک پروژه Console اضافه کنید.سپس 3 پکیج در آن نصب کنید.Install-Package Grpc.Tools -Version 2.34.0
Install-Package Grpc.Net.Client -Version 2.34.0
Install-Package Google.Protobuf -Version 3.14.0فایل های proto قراردادی هستند که باید هم در Server وجود داشته باشند و هم در Client و به 2 صورت مختلف هم Compile می شوند.1.کامپایل مخصوص Server2.کامپایل مخصوص Clientسپس باید فایل proto ای که در سرور قرار گرفته است را در Client هم اضافه کنیم، به صورت زیر اینکار را انجام دهید.بعد از اینکه فایل اضافه شد، Solution را یکبار Build کنید.(خیلی مهم)در این مرحله  باید بتونیم متد Sum رو در برنامه Console ای که ساختیم استفاده کنیم.ابتدا لازم است که یک ارتباط با سرور برقرار کنیم و سپس میتوانیم متد Sum را در برنامه خودمان فراخوانی کنیم، آرگومان ورودی اش را باید حتما بهش پاس بدیم. https://gist.github.com/ArminShoeibi/837347164542cba523e49ebc5143649a خب الان که هم سرور و هم کلاینت کدهاشون رو نوشتیم باید فقط اجراشون کنیم.ولی اول باید ترتیب اجرای پروژه ها را  تنظیم کنیم و سپس اجرایشان کنیم.بعد از اینکه ترتیب اجرا پروژه ها را تنظیم کردید، روی Start کلیک کنید.در ادامه اگر HTTPS certificate بر روی سیستم تون نصب نشده باشه، این پیام رو خواهید دید، بر روی Yes کلیک کنید.و  دوباره بر روی Yes کلیک کنید، تا پروژه ها اجرا شوند.و همینطور که می بینید، 2 عدد  از سمت کلاینت به سرور ارسال شد   و سرور و جواب جمع  دو عدد رو بهمون داد و ما جواب رو چاپ کردیم.در قسمت های بعدی به طور کامل و عمیق در مورد تمامی اجزای gRPC و کامپایلر آن یعنی protoc صحبت خواهیم کرد.سورس کد پروژه: https://github.com/ArminShoeibi/DNZ.Calculator-gRPC مقالات بیشتر در کانال دات نت زومhttps://t.me/DotNetZoom</description>
                <category>دات نت زوم</category>
                <author>آرمین شعیبی</author>
                <pubDate>Mon, 11 Jan 2021 19:20:27 +0330</pubDate>
            </item>
            </channel>
</rss>