مطالب
React 16x - قسمت 34 - توزیع برنامه
در قسمت آخر این سری، نگاهی خواهیم داشت به نحوه‌ی توزیع برنامه‌های React و نکات مرتبط با آن.


افزودن متغیرهای محیطی

در برنامه‌ی نمایش لیست فیلم‌هایی که تا قسمت 29 آن‌را بررسی کردیم، از فایل src\config.json برای ذخیره سازی اطلاعات تنظیمات برنامه استفاده شد. هرچند این روش کار می‌کند اما بر اساس محیط‌های مختلف توسعه، متغیر نیست. اغلب برنامه‌ها باید بتوانند حداقل در سه محیط توسعه، آزمایش و تولید، بر اساس متغیرها و تنظیمات خاص هر کدام، کار کنند. برای مثال بر روی سیستمی که کار توسعه در آن انجام می‌شود، می‌خواهیم apiUrl متفاوتی را نسبت به حالتیکه برنامه توزیع می‌شود، داشته باشیم.
برای رفع این مشکل، برنامه‌هایی که توسط create-react-app تولید می‌شوند، دارای پشتیبانی توکاری از متغیرهای محیطی هستند. برای این منظور نیاز است در ریشه‌ی پروژه (جائیکه فایل package.json قرار دارد) فایل جدید env. را ایجاد کرد. در ویندوز برای ایجاد یک چنین فایل‌هایی که فقط از یک پسوند تشکیل می‌شوند، باید نام فایل را به صورت .env. وارد کرد؛ سپس خود ویندوز نقطه‌ی نهایی را حذف می‌کند. البته اگر از ادیتور VSCode برای ایجاد این فایل استفاده می‌کنید، نیازی به درج نقطه‌ی انتهایی نیست. در این فایل environment ایجاد شده می‌توان تمام متغیرهای محیطی مورد نیاز را با مقادیر پیش‌فرض آن‌ها درج کرد. همچنین می‌توان این مقادیر پیش‌فرض را بر اساس محیط‌های مختلف کاری، بازنویسی کرد. برای مثال می‌توان فایل env.development. را اضافه کرد؛ به همراه فایل‌های env.test. و env.production.


متغیرهای محیطی به صورت key=value درج می‌شوند. این کلیدها نیر باید با REACT_APP_ شروع شوند؛ در غیر اینصورت، کار نخواهند کرد. برای مثال در فایل env.، دو متغیر پیش‌فرض زیر را تعریف می‌کنیم:
REACT_APP_NAME=My App
REACT_APP_VERSION=1
اکنون برای خواندن این متغیرها برای مثال در فایل index.js (و یا هر فایل جاوا اسکریپتی دیگری در برنامه)، سطر زیر را درج می‌کنیم:
console.log(process.env);
process به معنای پروسه‌ی جاری برنامه‌است (و مرتبط است به پروسه‌ی node.js ای که برنامه‌ی React را اجرا می‌کند) و خاصیت env، به همراه تمام متغیرهای محیطی برنامه می‌باشد. در این حالت اگر برنامه را اجرا کنیم، در کنسول توسعه دهندگان مرورگر، به یک چنین خروجی خواهیم رسید:


در این خروجی، متغیر "NODE_ENV: "development به صورت خودکار با تولید بسته‌های مخصوص ارائه‌ی نهایی، به production تنظیم می‌شود. سایر متغیرهای محیطی تعریف شده را نیز در اینجا ملاحظه می‌کنید. با توجه به خواص شیء env، برای مثال جهت دسترسی به نام برنامه می‌توان از مقدار process.env.REACT_APP_NAME استفاده کرد.


یک نکته: با هر تغییری در مقادیر متغیرهای محیطی، نیاز است یکبار دیگر برنامه را از ابتدا توسط دستور npm start، راه اندازی مجدد کرد؛ چون این فایل‌ها به صورت خودکار ردیابی نمی‌شوند.


نحوه‌ی پردازش متغیرهای محیطی درج شده‌ی در برنامه

اگر همان سطر لاگ کردن خروجی process.env را به صورت زیر تغییر دهیم:
console.log("My App Name", process.env.REACT_APP_NAME);
و برنامه را مجددا اجرا کنیم، با مراجعه‌ی به برگه‌ی Sources و انتخاب مسیر localhost:3000/static/js/main.chunk.js و سپس جستجوی "My App Name" ای که در اینجا اضافه کردیم (با فشردن دکمه‌های Ctrl+F)، به خروجی زیر خواهیم رسید:


همانطور که مشاهده می‌کنید، فراخوانی console.log ما، دیگر به همراه متغیر process.env.REACT_APP_NAME نیست؛ بلکه مقدار اصلی این متغیر در اینجا درج شده‌است. بنابراین اگر در در حین توسعه‌ی برنامه، از متغیرهای محیطی استفاده شود، این متغیرها با مقادیر اصلی آن‌ها در حین پروسه‌ی Build نهایی، جایگزین می‌شوند.


Build برنامه‌های React برای محیط تولید

اجرای دستور npm start، سبب ایجاد یک Build مخصوص محیط توسعه می‌شود که بهینه سازی نشده‌است و به همراه اطلاعات اضافی قابل توجهی جهت دیباگ ساده‌تر برنامه‌است. برای رسیدن به یک خروجی بهینه سازی شده‌ی مخصوص محیط تولید و ارائه‌ی نهایی باید دستور npm run build را در خط فرمان اجرا کرد. خروجی نهایی این دستور، در پوشه‌ی جدید build واقع در ریشه‌ی پروژه، قرار می‌گیرد. اکنون می‌توان کل محتویات این پوشه را جهت ارائه‌ی نهایی در وب سرور خود، مورد استفاده قرار داد.
پس از پایان اجرای دستور npm run build، پیام «امکان ارائه‌ی آن توسط static server زیر نیز وجود دارد» ظاهر می‌شود:
> npm install -g serve
> serve -s build
اگر علاقمند باشید تا خروجی حالت production تولید شده را نیز به صورت محلی آزمایش کنید، ابتدا باید static server یاد شده را توسط دستور npm install فوق نصب کنید. سپس ریشه‌ی پروژه را در خط فرمان باز کرده و دستور serve -s build را صادر کنید (البته اگر با خط فرمان به پوشه‌ی build وارد شدید، دیگر نیازی به ذکر پوشه‌ی build نخواهد بود). اکنون می‌توانید برنامه را در آدرس http://localhost:5000 در مرورگر خود بررسی نمائید.

البته با توجه به اینکه backend سرور برنامه‌های ما نیز در همین آدرس قرار دارد و در صورت ورود این آدرس، به صورت خودکار به https://localhost:5001/index.html هدایت خواهید شد، می‌توان این پورت پیش‌فرض را با اجرای دستور  serve -s build -l 1234 تغییر داد. اکنون می‌توان آدرس جدید http://localhost:1234 را در مرورگر آزمایش کرد که ... با خطای زیر کار نمی‌کند:
Access to XMLHttpRequest at 'https://localhost:5001/api/genres' from origin 'http://localhost:1234' has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
روش رفع این مشکل را در قسمت 23 بررسی کردیم و در اینجا جهت بهبود آن می‌توان متد WithOrigins فایل Startup.cs را به صورت زیر تکمیل کرد:
WithOrigins("http://localhost:3000", "http://localhost:1234")

یک نکته: زمانیکه از دستور npm start استفاده می‌شود، متغیرهای محیطی از فایل env.development. خوانده خواهند شد و زمانیکه از دستور npm run build استفاده می‌شود، این متغیرها از فایل env.production. تامین می‌شوند. در این حالت‌ها اگر متغیری در این دو فایل درج نشده بود، از مقدار پیش‌فرض موجود در فایل env. استفاده می‌گردد. از فایل env.test. با اجرای دستور npm test، به صورت خودکار استفاده می‌شود.


آماده سازی برنامه‌ی React، برای توزیع نهایی

تا اینجا برنامه‌ی React تهیه شده، اطلاعات apiUrl خودش را از فایل config.json دریافت می‌کند. اکنون می‌خواهیم بر اساس حالات مختلف توسعه و تولید، از apiUrlهای متفاوتی استفاده شود. به همین جهت به فایل env.production. مراجعه کرده و تنظیمات ذیل را به آن اضافه می‌کنیم:
REACT_APP_API_URL=https://localhost:5001/api
REACT_APP_ADMIN_ROLE_NAME=Admin
البته فعلا همین متغیرها را به فایل env.development. نیز می‌توان اضافه کرد؛ چون backend سرور ما در هر دو حالت، در این مثال، در آدرس فوق قرار دارد.

اکنون به برنامه مراجعه کرده و در هرجائی که ارجاعی به فایل config.json وجود دارد، سطر import آن‌را حذف می‌کنیم. با این تغییر، تمام آدرس‌هایی مانند:
const apiEndpoint = apiUrl + "/users";
را به صورت زیر ویرایش می‌کنیم:
const apiEndpoint =  "/users";
در ادامه برای تامین مقدار apiUrl به صورت خودکار، به فایل src\services\httpService.js مراجعه کرده و در ابتدای آن، یک سطر زیر را اضافه می‌کنیم:
 axios.defaults.baseURL = process.env.REACT_APP_API_URL;
به این ترتیب تمام درخواست‌های ارسالی توسط Axios، دارای baseURL ای خواهند شد که از فایل متغیر محیطی جاری تامین می‌شود. همانطور که پیش‌تر نیز عنوان شد، این مقدار در زمان Build، با مقدار ثابتی که از فایل env جاری خوانده می‌شود، جایگزین خواهد شد.
همچنین adminRoleName مورد نیاز در فایل src\services\authService.js را نیز از همان فایل env جاری تامین می‌کنیم:
const adminRoleName =  process.env.REACT_APP_ADMIN_ROLE_NAME;
پس از این تغییرات، نیاز است برای حالت توسعه، یکبار دیگر دستور npm start و یا برای حالت تولید، دستور npm run build را اجرا کرد تا اطلاعات درج شده‌ی در فایل‌های env.، پردازش و جایگزین شوند.


کدهای کامل این قسمت را از اینجا می‌توانید دریافت کنید: sample-34-frontend.zip و sample-34-backend.zip
مطالب
Best Practice هایی برای طراحی RESTful API - قسمت اول

با آمدن Asp.Net Web API کار ساختن Web API‌ها برای برنامه نویس‌ها به خصوص دسته ای که با ساخت API و وب سرویس آشنا نبودند خیلی ساده‌تر شد . اگر با Asp.Net MVC آشنا باشید خیلی سریع می‌توانید اولین Web Service خودتان را بسازید .

در صفحه مربوط به Asp.Net Web API آمده است که این فریمورک بستر مناسبی برای ساخت و توسعه برنامه ‌های RESTful است . اما تنها ساختن کنترلر و اکشن و برگشت دادن داده‌ها به سمت کلاینت ، به خودی خود برنامه شما رو تبدیل به یک RESTful API نمی‌کند .

مثل تمام مفاهیم و ابزارها ، طراحی و ساختن RESTful API هم دارای اصول و Best Practice هایی است که رعایت آنها به خصوص در این زمینه از اهمیت زیادی برخوردار است . همانطور که از تعریف API برمی آید شما در حال طراحی رابطی هستید تا به توسعه دهندگان دیگر امکان دهید از داده‌ها و یا خدمات شما در برنامه‌ها و سرویس هایشان استفاده کنند . مانند API‌های توئیتر و نقشه گوگل که برنامه‌های زیادی بر مبنای آنها ساخته شده اند . در واقع  توسعه دهندگان مشتریان API شما هستند .

بهره وری توسعه دهنده مهمترین اصل

اینطور می‌توان نتیجه گرفت که اولین و مهمترین اصل در طراحی API باید رضایت و موفقیت توسعه دهنده در درک و یادگیری سریع API شما ،نه تنها با کمترین زحمت بلکه همراه با حس نشاط ، باشد. ( تجربه کاربری در اینجا هم می‌تواند صدق کند ). سعی کنید در زمان انتخاب از بین روش‌های طراحی موجود ، از دیدگاه توسعه دهنده به مسئله نگاه کنید . خود را به جای او قرار دهید و تصور کنید که می‌خواهید با استفاده از API موجود یک رابط کاربری طراحی کنید یا یک اپلیکشن برای موبایل بنویسید و اصل را این نکته قرار دهید که بهره وری برنامه نویس را حداکثر کنید. ممکن است گاهی بین طرحی که بر اساس این اصل برای API خود در نظر داریم و یکی از اصول یا استانداردها تعارض بوجود بیاید . در این موارد بعد از اینکه مطمئن شدیم این اختلاف ناشی از طراحی و درک اشتباه خودمان نیست (که اکثرا هست ) ارجحیت را باید به طراحی بدهیم . 


تهیه مستندات API

اگر برای پروژه وب سایتتان هیچ نوشته ای یا توضیحی ندارید ، جالب نیست اما خودتان ساختار برنامه خود را می‌شناسید و کار را پیش می‌برید. اما توسعه دهنده ای که از API شما می‌خواهد استفاده کند و به احتمال زیاد شما را نمی‌شناسد ، عضو تیم شما هم نیست ، هیچ ایده ای درباره ساختار آن ، روش نامگذاری توابع و منابع، ساختار Url‌‌ها ، چگونگی و گام‌های پروسه درخواست تا دریافت پاسخ ندارد ،و به مستندات شما وابسته است و تمام اینها باید در مستندات شما باشد. بیشتر توسعه دهنده‌ها قبل از تست کردن API شما سری به مستندات می‌زنند ، دنبال نمونه کد آموزشی می‌گردند و در اینترنت درباره آن جستجو می‌کنند . ازینرو مستندات ( کارامد ) یک ضرورت است :
1- در مستندات باید هم درباره کلیت و هم در مورد تک تک توابع ( پارامترهای معتبر ، ساختار پاسخ‌ها و ... ) توضیحات وجود داشته باشد.
2- باید شامل مثالهایی از سیکل کامل درخواست‌ها / پاسخ‌ها باشند .
3- تغییرات اعمال شده نسبت به نسخه‌های قبلی باید در مستندات بیان شوند .
4- (در وب ) یافتن و جستجو کردن در مستنداتی که به صورت فایل Pdf هستند یا برای دسترسی نیاز به Login داشته باشند سخت و آزاردهنده هستند.
5- کسی را داشته باشید تا با و بدون مستندات با API شما کار کند و از این روش برای تکمیل و اصلاح مستندات استفاده کنید.

رعایت نسخه بندی و حفظ نسخه‌های قبلی به صورت فعال برای مدت معین
یک API  تقریبا هیچوقت کاملا پایدار نمی‌شود و اعمال تغییرات برای بهبود آن اجتناب ناپذیر هستند . مسئله مهم این است که چطور این تغییرات مدیریت شوند . مستند کردن تغییرات ، اعلام به موقع آنها و دادن یک بازه زمانی کافی برای ارتقا یافتن برنامه هایی که از نسخه‌های قدیمی‌تر استفاده می‌کنند نکات مهمی هستند . همیشه در کنار نسخه بروز و اصلی یک یا دو نسخه ( بسته به API و کلاینت‌های آن ) قدیمی‌تر را برای زمان مشخصی در حالت سرویس دهی داشته باشید .

داشتن یک روش مناسب برای اعلام تغییرات و ارائه مستندات و البته دریافت بازخورد از استفاده کنندگان
تعامل با کاربران برنامه باید از کانال‌های مختلف وجود داشته باشد .از وبلاگ ، Mailing List ، Google Groups و دیگر ابزارهایی که در اینترنت وجود دارند برای انتشار مستندات ، اعلام بروزرسانی‌ها ، قرار دادن مقالات و نمونه کدهای آموزشی ، پرسش و پاسخ با کاربران استفاده کنید .

مدیریت خطاها به شکل صحیح که به توسعه دهنده در آزمودن برنامه اش کمک کند.
از منظر برنامه نویسی که از API شما استفاده می‌کند هرآنچه در آنسوی API اتفاق می‌افتد یک جعبه سیاه است . به همین جهت خطاهای API شما ابزار کلیدی برای او هستند که خطایابی و اصلاح برنامه در حال توسعه اش را ممکن می‌کنند . علاوه بر این ، زمانی که برنامه نوشته شده با API شما مورد استفاده کاربر نهایی قرار گرفت ، خطاهای به دقت طراحی شده API شما کمک بزرگی برای توسعه دهنده در عیب یابی هستند .
1- از Status Code های HTTP استفاده کنید و سعی کنید تا حد ممکن آنها را نزدیک به مفهوم استانداردشان بکار ببرید .
2- خطا و علت آن را به زبان روشن توضیح دهید و در توضیح خساست به خرج ندهید .
3- در صورت امکان لینکی به یک صفحه وب که حاوی توضیحات بیشتری است را در خطا بگنجانید .

رعایت ثبات و یکدستی در تمام بخش‌های طراحی که توانایی پیش بینی توسعه دهنده را در استفاده از API افزایش می‌دهد .
داشتن مستندات لازم است اما این بدین معنی نیست که خود API نباید خوانا و قابل پیش بینی باشد . از هر روش و تکنیکی که استفاده می‌کنید آن را در تمام پروژه حفظ کنید . نامگذاری توابع/منابع ، ساختار پاسخ‌ها ، Url‌‌ها ، نقش و عملیاتی که HTTP method‌‌ها در API شما انجام می‌دهند باید ثبات داشته باشند . از این طریق توسعه دهنده لازم نیست برای هر بخشی از API شما به سراغ فایل‌ها راهنما برود . و به سرعت کار خود را به پیش می‌برد .

انعطاف پذیر بودن API
API توسط کلاینت‌های مختلفی و برای افراد مختلفی مورد استفاده قرار می‌گیرد که لزوما همه‌ی آنها ساختار یکسانی ندارند و API شما باید تا جای ممکن بتواند همه آنها را پوشش دهد . محدود بودن فرمت پاسخ ، ثابت بودن فیلدهای ارسالی به کلاینت ، ندادن امکان صفحه بندی ، مرتب سازی و جستجو در داده‌ها به کلاینت ، داشتن تنها یک نوع احراز هویت ، وابسته بودن به کوکی و ... از مشخصات یک API منجمد و انعطاف ناپذیر هستند . 

اینها اصولی کلی بودند که بسیاری از آنها مختص طراحی API نیستند و در تمام حوزه‌ها قابل استفاده بوده ، جز الزامات هستند . در قسمت‌های بعدی نکات اختصاصی‌تری را بررسی خواهیم کرد .
مطالب
فلسفه وجودی بخش finally در try catch چیست؟
حتما شما هم متوجه شدید که وقتی رخداد یک استثناء را با استفاده از try و catch کنترل می‌کنیم، هر چیزی که بعد از بسته شدن تگ catch بنویسیم، در هر صورت اجرا می‌شود.

 try {
     int i=0;
     string s = "hello";
     i = Convert.ToInt32(s);
} catch (Exception ex)
{
     Console.WriteLine("Error");
}
Console.WriteLine("I am here!");

پس فلسفه استفاده از بخش finally چیست؟
در قسمت finally منابع تخصیص داده شده در try را آزاد می‌کنیم. کد موجود در این قسمت به هر روی اجرا می‌شود چه استثناء رخ دهد چه ندهد. البته اگر استثناء رخ داده شده در لیست استثناء هایی که برای آنها catch انجام دادیم نباشد، قسمت finally هم عمل نخواهد کرد مگر اینکه از catch به صورت سراسری استفاده کنیم.
اما مهمترین مزیتی که finally ایجاد می‌کند در این است که حتی اگر در قسمت try با استفاده از دستوراتی مثل return یا break یا continue از ادامه کد منصرف شویم و مثلا مقداری برگردانیم، چه خطا رخ دهد یا ندهد کد موجود در finally اجرا می‌شود در حالی که کد نوشته شده بعد از try catch finally فقط در صورتی اجرا می‌شود که به طور منطقی اجرای برنامه به آن نقطه برسد. اجازه بدهید با یک مثال توضیح دهم. اگر کد زیر را اجرا کنیم:
  public static int GetMyInt()
 {
     try {
          for (int i=10;i>=0;i--)
             Console.WriteLine(10/i);
         return 1;
     } catch
     {
         Console.WriteLine("Error!");
     }
     finally {
         Console.WriteLine("ok");
     }
     Console.WriteLine("can you reach here?");  
     return -1;  
 }

برنامه خطای تقسیم بر صفر می‌دهد اما با توجه به کدی که نوشتیم، عدد -1 به خروجی خواهد رفت. در عین حال عبارت ok و can you reach here در خروجی چاپ شده است. اما حال اگر مشکل تقسیم بر صفر را حل کنیم، آیا باز هم عبارت can you reach here در خروجی چاپ خواهد شد؟
 
public static int GetMyInt()
 {
     try {
          for (int i=10;i>=1;i--)
             Console.WriteLine(10/i);
         return 1;
     } catch
     {
         Console.WriteLine("Error!");
     }
     finally {
         Console.WriteLine("ok");
     }
     Console.WriteLine("can you reach here?");  
     return -1;  
 }

مشاهده می‌کنید که مقدار 1 برگردانده می‌شود و عبارت can you reach here در خروجی چاپ نمی‌شود ولی همچنان عبارت ok که در finally ذکر شده در خروجی چاپ می‌شود. یک مثال خوب استفاده از چنین وضعیتی، زمانی است که شما یک ارتباط با بانک اطلاعاتی باز می‌کنید، و نتیجه یک عملیات را با دستور return به کاربر بر می‌گردانید. مسئله این است که در این وضعیت چگونه ارتباط با دیتابیس بسته شده و منابع آزاد می‌گردند؟ اگر در حین عملیات بانک اطلاعاتی، خطایی رخ دهد یا ندهد، و شما دستور آزاد سازی منابع و بستن ارتباط را در داخل قسمت finally نوشته باشید، وقتی دستور return فراخوانی می‌شود، ابتدا منابع آزاد و سپس مقدار به خروجی بر می‌گردد.
public int GetUserId(string nickname)
{
     SqlConnection connection = new SqlConnection(...);
     SqlCommand command = connection.CreateCommand();
     command.CommandText = "select id from users where nickname like @nickname";
     command.Parameters.Add(new SqlParameter("@nickname", nickname));
      try {
         connection.Open();
          return Convert.ToInt32(command.ExecuteScalar());
      } 
      catch(SqlException exception)
      {
      // some exception handling
         return -1;
      } finally {
          if (connection.State == ConnectionState.Open)
         connection.Close();
      }
      // if all things works, you can not reach here
}


مطالب
بررسی تغییرات Blazor 8x - قسمت یازدهم - قالب جدید پیاده سازی اعتبارسنجی و احراز هویت - بخش اول
قالب‌های پیش‌فرض Blazor 8x، به همراه قسمت بازنویسی شده‌ی ASP.NET Core Identity برای Blazor هم هستند که در این قسمت به بررسی نحوه‌ی عملکرد آن‌ها می‌پردازیم.


معرفی قالب‌های جدید شروع پروژه‌های Blazor در دات نت 8 به همراه قسمت Identity

در قسمت دوم این سری، با قالب‌های جدید شروع پروژه‌های Blazor 8x آشنا شدیم و هدف ما در آنجا بیشتر بررسی حالت‌های مختلف رندر Blazor در دات نت 8 بود. تمام این قالب‌ها به همراه یک سوئیچ دیگر هم به نام auth-- هستند که توسط آن و با مقدار دهی Individual که به معنای Individual accounts است، می‌توان کدهای پیش‌فرض و ابتدایی Identity UI جدید را نیز به قالب در حال ایجاد، به صورت خودکار اضافه کرد؛ یعنی به صورت زیر:

اجرای قسمت‌های تعاملی برنامه بر روی سرور؛ به همراه کدهای Identity:
dotnet new blazor --interactivity Server --auth Individual

اجرای قسمت‌های تعاملی برنامه در مرورگر، توسط فناوری وب‌اسمبلی؛ به همراه کدهای Identity:
dotnet new blazor --interactivity WebAssembly --auth Individual

برای اجرای قسمت‌های تعاملی برنامه، ابتدا حالت Server فعالسازی می‌شود تا فایل‌های WebAssembly دریافت شوند، سپس فقط از WebAssembly استفاده می‌کند؛ به همراه کدهای Identity:
dotnet new blazor --interactivity Auto --auth Individual

فقط از حالت SSR یا همان static server rendering استفاده می‌شود (این نوع برنامه‌ها تعاملی نیستند)؛ به همراه کدهای Identity:
dotnet new blazor --interactivity None --auth Individual

 و یا توسط پرچم all-interactive--، که interactive render mode را در ریشه‌ی برنامه قرار می‌دهد؛ به همراه کدهای Identity:
 dotnet new blazor --all-interactive --auth Individual

یک نکته: بانک اطلاعاتی پیش‌فرض مورد استفاده‌ی در این نوع پروژه‌ها، SQLite است. برای تغییر آن می‌توانید از پرچم use-local-db-- هم استفاده کنید تا از LocalDB بجای SQLite استفاده کند.


Identity UI جدید Blazor در دات نت 8، یک بازنویسی کامل است


در نگارش‌های قبلی Blazor هم امکان افزودن Identity UI، به پروژه‌های Blazor وجود داشت (اطلاعات بیشتر)، اما ... آن پیاده سازی، کاملا مبتنی بر Razor pages بود. یعنی کاربر، برای کار با آن مجبور بود از فضای برای مثال Blazor Server خارج شده و وارد فضای جدید ASP.NET Core شود تا بتواند، Identity UI نوشته شده‌ی با صفحات cshtml. را اجرا کند و به اجزای آن دسترسی پیدا کند (یعنی عملا آن قسمت UI اعتبارسنجی، Blazor ای نبود) و پس از اینکار، مجددا به سمت برنامه‌ی Blazor هدایت شود؛ اما ... این مشکل در دات نت 8 با ارائه‌ی صفحات SSR برطرف شده‌است.
همانطور که در قسمت قبل نیز بررسی کردیم، صفحات SSR، طول عمر کوتاهی دارند و هدف آن‌ها تنها ارسال یک HTML استاتیک به مرورگر کاربر است؛ اما ... دسترسی کاملی را به HttpContext برنامه‌ی سمت سرور دارند و این دقیقا چیزی است که زیر ساخت Identity، برای کار و تامین کوکی‌های مورد نیاز خودش، احتیاج دارد. صفحات Identity UI از یک طرف از HttpContext برای نوشتن کوکی لاگین موفقیت آمیز در مرورگر کاربر استفاده می‌کنند (در این سیستم، هرجائی متدهای XyzSignInAsync وجود دارد، در پشت صحنه و در پایان کار، سبب درج یک کوکی اعتبارسنجی و احراز هویت در مرورگر کاربر نیز خواهد شد) و از طرف دیگر با استفاده از میان‌افزارهای اعتبارسنجی و احراز هویت ASP.NET Core، این کوکی‌ها را به صورت خودکار پردازش کرده و از آن‌ها جهت ساخت خودکار شیء User قابل دسترسی در این صفحات (شیء context.User.Identity@)، استفاده می‌کنند.
به همین جهت تمام صفحات Identity UI ارائه شده در Blazor 8x، از نوع SSR هستند و اگر در آن‌ها از فرمی استفاده شده، دقیقا همان فرم‌های تعاملی است که در قسمت چهارم این سری بررسی کردیم. یعنی صرفا بخاطر داشتن یک فرم، ضرورتی به استفاده‌ی از جزایر تعاملی Blazor Server و یا Blazor WASM را ندیده‌اند و اینگونه فرم‌ها را بر اساس مدل جدید فرم‌های تعاملی Blazor 8x پیاده سازی کرده‌اند. بنابراین این صفحات کاملا یکدست هستند و از ابتدا تا انتها، به صورت یکپارچه بر اساس مدل SSR کار می‌کنند (که در اصل خیلی شبیه به Razor pages یا Viewهای MVC هستند) و جزیره، جزیره‌ای، طراحی نشده‌اند.

 
روش دسترسی به HttpContext در صفحات SSR

اگر به کدهای Identity UI قالب آغازین یک پروژه که در ابتدای بحث روش تولید آن‌ها بیان شد، مراجعه کنید، استفاده از یک چنین «پارامترهای آبشاری» را می‌توان مشاهده کرد:

@code {

    [CascadingParameter]
    public HttpContext HttpContext { get; set; } = default!;
متد ()AddRazorComponents ای که در فایل Program.cs اضافه می‌شود، کار ثبت CascadingParameter ویژه‌ی فوق را به صورت زیر انجام می‌دهد که در Blazor 8x به آن یک Root-level cascading value می‌گویند:
services.TryAddCascadingValue(sp => sp.GetRequiredService<EndpointHtmlRenderer>().HttpContext);
مقادیر آبشاری برای ارسال اطلاعاتی بین درختی از کامپوننت‌ها، از یک والد به چندین فرزند که چندین لایه ذیل آن واقع شده‌‌اند، مفید است. برای مثال فرض کنید می‌خواهید اطلاعات عمومی تنظیمات یک کاربر را به چندین زیر کامپوننت، ارسال کنید. یک روش آن، ارسال شیء آن به صورت پارامتر، به تک تک آن‌ها است و راه دیگر، تعریف یک CascadingParameter است؛ شبیه به کاری که در اینجا انجام شده‌است تا دیگر نیازی نباشد تا تک تک زیر کامپوننت‌ها این شیء را به یک لایه زیریرین خود، یکی یکی منتقل کنند.

در کدهای Identity UI ارائه شده، از این CascadingParameter برای مثال جهت خروج از برنامه (HttpContext.SignOutAsync) و یا دسترسی به اطلاعات هدرهای رسید (if (HttpMethods.IsGet(HttpContext.Request.Method)))، دسترسی به سرویس‌ها (()<HttpContext.Features.Get<ITrackingConsentFeature)، تامین مقدار Cancellation Token به کمک HttpContext.RequestAborted و یا حتی در صفحه‌ی جدید Error.razor که آن نیز یک صفحه‌ی SSR است، برای دریافت HttpContext?.TraceIdentifier استفاده‌ی مستقیم شده‌است.

نکته‌ی مهم: باید به‌خاطر داشت که فقط و فقط در صفحات SSR است که می‌توان به HttpContext به نحو آبشاری فوق دسترسی یافت و همانطور که در قسمت قبل نیز بررسی شد، سایر حالات رندر Blazor از دسترسی به آن، پشتیبانی نمی‌کنند و اگر چنین پارامتری را تنظیم کردید، نال دریافت می‌کنید.


بررسی تفاوت‌های تنظیمات ابتدایی قالب جدید Identity UI در Blazor 8x با نگارش‌های قبلی آن

مطالب و نکات مرتبط با قالب قبلی را در مطلب «Blazor 5x - قسمت 22 - احراز هویت و اعتبارسنجی کاربران Blazor Server - بخش 2 - ورود به سیستم و خروج از آن» می‌توانید مشاهده کنید.

در قسمت سوم این سری، روش ارتقاء یک برنامه‌ی قدیمی Blazor Server را به نگارش 8x آن بررسی کردیم و یکی از تغییرات مهم آن، حذف فایل‌های cshtml. ای آغاز برنامه و انتقال وظایف آن، به فایل جدید App.razor و انتقال مسیریاب Blazor به فایل جدید Routes.razor است که پیشتر در فایل App.razor نگارش‌های قبلی Blazor وجود داشت.
در این نگارش جدید، محتوای فایل Routes.razor به همراه قالب Identity UI به صورت زیر است:
<Router AppAssembly="@typeof(Program).Assembly">
    <Found Context="routeData">
        <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(Layout.MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
</Router>
در نگارش‌های قبلی، این مسیریاب داخل کامپوننت CascadingAuthenticationState محصور می‌شد تا توسط آن بتوان AuthenticationState را به تمام کامپوننت‌های برنامه ارسال کرد. به این ترتیب برای مثال کامپوننت AuthorizeView، شروع به کار می‌کند و یا توسط شیء context.User می‌توان به User claims دسترسی یافت و یا به کمک ویژگی [Authorize]، دسترسی به صفحه‌ای را محدود به کاربران اعتبارسنجی شده کرد.
اما ... در اینجا با یک نگارش ساده شده سروکار داریم؛ از این جهت که برنامه‌های جدید، به همراه جزایر تعاملی هم می‌توانند باشند و باید بتوان این AuthenticationState را به آن‌ها هم ارسال کرد. به همین جهت مفهوم جدید مقادیر آبشاری سطح ریشه (Root-level cascading values) که پیشتر در این بحث معرفی شد، در اینجا برای معرفی AuthenticationState استفاده شده‌است.

در اینجا کامپوننت جدید FocusOnNavigate را هم مشاهده می‌کنید. با استفاده از این کامپوننت و براساس CSS Selector معرفی شده، پس از هدایت به یک صفحه‌ی جدید، این المان مشخص شده دارای focus خواهد شد. هدف از آن، اطلاع رسانی به screen readerها در مورد هدایت به صفحه‌ای دیگر است (برای مثال، کمک به نابیناها برای درک بهتر وضعیت جاری).

همچنین در اینجا المان NotFound را هم مشاهده نمی‌کنید. این المان فقط در برنامه‌های WASM جهت سازگاری با نگارش‌های قبلی، هنوز هم قابل استفاده‌است. جایگزین آن‌را در قسمت سوم این سری، برای برنامه‌های Blazor server در بخش «ایجاد فایل جدید Routes.razor و مدیریت سراسری خطاها و صفحات یافت نشده» آن بررسی کردیم.


روش انتقال اطلاعات سراسری اعتبارسنجی یک کاربر به کامپوننت‌ها و جزایر تعاملی واقع در صفحات SSR

مشکل! زمانیکه از ترکیب صفحات SSR و جزایر تعاملی واقع در آن استفاده می‌کنیم ... جزایر واقع در آن‌ها دیگر این مقادیر آبشاری را دریافت نمی‌کنند و این مقادیر در آن‌ها نال است. برای حل این مشکل در Blazor 8x، باید مقادیر آبشاری سطح ریشه را (Root-level cascading values) به صورت زیر در فایل Program.cs برنامه ثبت کرد:
builder.Services.AddCascadingValue(sp =>new Preferences { Theme ="Dark" });
پس از این تغییر، اکنون نه فقط صفحات SSR، بلکه جزایر واقع در آن‌ها نیز توسط ویژگی [CascadingParameter] می‌توانند به این مقدار آبشاری، دسترسی داشته باشند. به این ترتیب است که در برنامه‌های Blazor، کامپوننت‌های تعاملی هم دسترسی به اطلاعات شخص لاگین شده‌ی توسط صفحات SSR را دارند. برای مثال اگر به فایل Program.cs قالب جدید Identity UI عنوان شده مراجعه کنید، چنین سطوری در آن قابل مشاهده هستند
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, IdentityRevalidatingAuthenticationStateProvider>();
متد جدید AddCascadingAuthenticationState، فقط کار ثبت AuthenticationStateProvider برنامه را به صورت آبشاری انجام می‌دهد.
این AuthenticationStateProvider سفارشی جدید هم در فایل اختصاصی IdentityRevalidatingAuthenticationStateProvider.cs پوشه‌ی Identity قالب پروژه‌های جدید Blazor 8x که به همراه اعتبارسنجی هستند، قابل مشاهده‌است.

یا اگر به قالب‌های پروژه‌های WASM دار مراجعه کنید، تعریف به این صورت تغییر کرده‌است؛ اما مفهوم آن یکی است:
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, PersistingServerAuthenticationStateProvider>();
در این پروژه‌ها، یک AuthenticationStateProvider سفارشی دیگری در فایل PersistingServerAuthenticationStateProvider.cs ارائه شده‌است (این فایل‌ها، جزو استاندارد قالب‌های جدید Identity UI پروژه‌های Blazor 8x هستند؛ فقط کافی است، یک نمونه پروژه‌ی آزمایشی را با سوئیچ جدید auth Individual-- ایجاد کنید، تا بتوانید این فایل‌های یاد شده را مشاهده نمائید).

AuthenticationStateProviderهای سفارشی مانند کلاس‌های IdentityRevalidatingAuthenticationStateProvider و PersistingServerAuthenticationStateProvider که در این قالب‌ها موجود هستند، چون به صورت آبشاری کار تامین AuthenticationState را انجام می‌دهند، به این ترتیب می‌توان به شیء context.User.Identity@ در جزایر تعاملی نیز به سادگی دسترسی داشت.

یعنی به صورت خلاصه، یکبار قرارداد AuthenticationStateProvider عمومی (بدون هیچ نوع پیاده سازی) به صورت یک Root-level cascading value ثبت می‌شود تا در کل برنامه قابل دسترسی شود. سپس یک پیاده سازی اختصاصی از آن توسط ()<builder.Services.AddScoped<AuthenticationStateProvider, XyzProvider به برنامه معرفی می‌شود تا آن‌را تامین کند. به این ترتیب زیر ساخت امن سازی صفحات با استفاده از ویژگی attribute [Authorize]@ و یا دسترسی به اطلاعات کاربر جاری با استفاده از شیء context.User@ و یا امکان استفاده از کامپوننت AuthorizeView برای نمایش اطلاعاتی ویژه به کاربران اعتبارسنجی شده مانند صفحه‌ی Auth.razor زیر، مهیا می‌شود:
@page "/auth"

@using Microsoft.AspNetCore.Authorization

@attribute [Authorize]

<PageTitle>Auth</PageTitle>

<h1>You are authenticated</h1>

<AuthorizeView>
    Hello @context.User.Identity?.Name!
</AuthorizeView>

سؤال: چگونه یک پروژه‌ی سمت سرور، اطلاعات اعتبارسنجی خودش را با یک پروژه‌ی WASM سمت کلاینت به اشتراک می‌گذارد (برای مثال در حالت رندر Auto)؟ این انتقال اطلاعات باتوجه به یکسان نبودن محل اجرای این دو پروژه (یکی بر روی کامپیوتر سرور و دیگری بر روی مرورگر کلاینت، در کامپیوتری دیگر) چگونه انجام می‌شود؟ این مورد را در قسمت بعد، با معرفی PersistentComponentState و «فیلدهای مخفی» مرتبط با آن، بررسی می‌کنیم.
اشتراک‌ها
dnSpy : ابزاری برای Debug و تغییر در DLL های دات نت

dnSpy is a debugger and .NET assembly editor. You can use it to edit and debug assemblies even if you don't have any source code available.

Some Features 

  • Debug .NET Framework, .NET Core and Unity game assemblies, no source code required
  • Set breakpoints and step into any assembly
  • Locals, watch, autos windows 
  • All metadata can be edited
  • Edit methods and classes in C# or Visual Basic with IntelliSense, no source code required
  • Add new methods, classes or members in C# or Visual Basic
  • IL editor for low level IL method body editing 

dnSpy : ابزاری برای Debug و تغییر در DLL های دات نت
مطالب
MongoDB #1

مروری بر MongoDB

MongoDB یک پایگاه داده سند-گرا (Document-Oriented) و مستقل از سکو است که کارائی بالا، دسترسی پذیری بالا و مقیاس پذیری آسانی را فراهم می‌کند. MongoDB بر اساس مفهوم مجموعه (Collection) و سند (Document) کار می‌کند.

پایگاه داده

پایگاه داده یک نگهدارنده‌ی فیزیکی برای مجموعه‌ها است. هر پایگاه داده مجموعه ای از فایل‌های خود را روی فایل سیستم دارد.  یک سرور MongoDB معمولا چندین پایگاه داده دارد. 

مجموعه

مجموعه یک گروه از سندهای MongoDB است. مجموعه معادل جدول در پایگاه داده‌های رابطه‌ای (Relational Database) است. یک مجموعه داخل یک پایگاه داده وجود دارد. مجموعه‌ها به شمای (Schema) تاکید ندارند. سندهای داخل مجموعه می‌توانند فیلدهای مختلفی داشته باشند. معمولا همه‌ی سندهای داخل یک مجموعه، شبیه یا مربوط به یک هدف هستند.

سند

یک سند مجموعه ای از جفت‌های کلید-مقدار (Key-Value Pairs) است. سند، شمای پویا دارد؛ یعنی سندها در مجموعه‌های مشابه نیازی به ساختار یا فیلدهای مشابه ندارند و فیلدهای مشترک در سند ممکن است نوع داده‌های متفاوتی را نگهداری کنند. جدول زیر مقایسه اصطلاحات پایگاه داده‌های رابطه‌ای و MongDB را نمایش می‌دهد:

  MongoDB   پایگاه داده رابطه ای
 پایگاه داده  پایگاه داده
 مجموعه    جدول
 سند  سطر
 فیلد  ستون
 سند توکار  ملحق کردن (Join)
کلید اصلی
(کلید پیش فرض  _id  توسط
MongoDB  فراهم شده)
 کلید اصلی
 پایگاه داده نسخه سرور و کلاینت  
Mongod Mysqld/Oracle 
mongo mysql/sqlplus 

مثالی از سند

در جدول زیر ساختار سند یک وبلاگ آمده است که جفت‌های کلید-مقدار بسادگی با کاما ازهم جدا شده اند.

{
   _id: ObjectId(7df78ad8902c)
   title: 'MongoDB Overview', 
   description: 'MongoDB is no sql database',
   by: 'tutorials point',
   url: 'http://www.tutorialspoint.com',
   tags: ['mongodb', 'database', 'NoSQL'],
   likes: 100, 
   comments: [
      {
         user:'user1',
         message: 'My first comment',
         dateCreated: new Date(2011,1,20,2,15),
         like: 0 
      },
      {
         user:'user2',
         message: 'My second comments',
         dateCreated: new Date(2011,1,25,7,45),
         like: 5
      }
   ]
}

_id یک 12بایتی هگزادسیمال است که یکتایی هر سند را اطمینان می‌دهد. شما می‌توانید یک _id را هنگام درج سند بسازید. اگر اینکار را نکنید، MongoDB یک شناسه‌ی یکتا را برای هر سند تهیه می‌کند. از این 12بایت، 4بایت اول آن مربوط به برچسب زمان جاری است، 3بایت بعدی برای شناسه‌ی ماشین، 2بایت بعدی برای شناسه‌ی پروسه MongoDB سرور و 3بایت باقیمانده یک مقدار صعودی ساده است. 

اشتراک‌ها
استفاده مناسب از Lazy Loading در ef

Entity Framework supports three ways to load related data - eager loading, lazy loading and explicit loading. The techniques shown in this topic apply equally to models created with Code First and the EF Designer. 

استفاده مناسب از Lazy Loading در ef
نظرات مطالب
مدیریت پیشرفته‌ی حالت در React با Redux و Mobx - قسمت چهارم - انجام اعمال async با Redux
سلام؛ من افزونه Redux DevTools  را در مروگر خودم نصب کردم و برنامه هم به درستی کار می‌کرد. اما وقتی همین برنامه را در مروگر دیگه تست کردم خطای " Cannot read property 'apply' of undefined  " را مشاهده کردم که با نصب افزونه  Redux DevTools مشکل برطرف شد. سوال اینجاست که وقتی برنامه را بخواهیم روی سرور پابلیش کنیم باید چه کاری انجام دهیم؟
نظرات مطالب
ذخیره سازی SQL تولیدی در NH3
البته یک چیزی خیلی برایم جالب است که ایشان به Underscore معتقد است:
http://10rem.net/articles/net-naming-conventions-and-programming-standards---best-practices

ولی در MSDN ذکر شده که
"Do not apply a prefix to field names or static field names"

منبع: پاراگراف آخر http://msdn.microsoft.com/en-us/library/ta31s3bc(v=vs.71).aspx
پاسخ به بازخورد‌های پروژه‌ها
خطای واردکردن(Import) فایل کلید
- برای کامپایل کردن سورس، نیازی به فایل pfx ندارید. راحت حذفش کنید و همچنین در قسمت خواص پروژه هم این فایل رو حذف کنید.
- راه دوم:
دستور زیر را  در خط فرمان VS.NET وارد کنید (البته run as admin فراموش نشود و فرمان باید در همان پوشه‌ای که فایل pfx هست اجرا شود):
sn -i key.pfx VS_KEY_2B6952079A0469C6
- راه سوم:
افزونه VSCommands را نصب کنید. دقیقا جایی که خطای فوق ظاهر می‌شود، کلیک راست کرده و گزینه apply fix را انتخاب کنید. همان راه دوم را به صورت خودکار اعمال می‌کند.