مطالب
تفاوت سیستم‌های یکپارچه با میکروسرویس‌ها (Monolithic vs Microservices architecture)

 معماری میکرو سرویس یا یکپارچه؟

برای درک میکروسرویس‌ها، باید بدانیم کاربرد سیستم‌های یکپارچه چیست و چه چیزی باعث شد در زمان‌های اخیر از برنامه‌های یکپارچه به میکروسرویس‌ها حرکت کنیم.


 سیستم‌های یکپارچه ( Monolithic applications  )

اگر تمام عملکردهای یک پروژه در یک بخش واحد وجود داشته باشند، آن برنامه به عنوان یک برنامه‌ی یکپارچه شناخته می‌شود. ما برنامه‌ی خود را در لایه‌های مختلفی مانند Presentation ، Service ، UI  طراحی می‌کنیم و سپس آن بخش از کدهای نوشته شده را به عنوان یک فایل خروجی به کار می‌گیریم. این چیزی نیست جز یک برنامه‌ی یکپارچه، که در آن " mono " یک پایگاه کد منفرد حاوی تمام عملکردهای مورد نیاز را نشان می‌دهد.


چرا اصلا به سمت میکروسرویس‌ها برویم؟

خب برای جواب به این سوال بهتر است معایب سیستم‌های یکپارچه را مرور کنیم:

  1. مدیریت دشوار بخاطر گسترش برنامه در گذشت زمان
  2. برای تغییری کوچک، کل برنامه را دوباره باید منتشر ( publish ) کنیم
  3. با تغییر و آپدیت برنامه، زمان انتشار افزایش می‌یابد.
  4. درک دشوار برای توسعه دهنده‌های جدید هر پروژه
  5. برای تقسیم ترافیک روی قسمت‌های مختلف برنامه، باید نمونه‌های کل برنامه را در چندین سرور منتشر کنیم که بسیار ناکارآمد و باعث استفاده‌ی بیهوده از منابع میشود
  6. اگر از فناوری یا تکنولوژی‌های جدید استفاده کنیم، برای عملکردی خاص، چه از نظر هزینه و چه از نظر زمان، بر کل برنامه تاثیر گذار است
  7. و در نهایت وجود یک باگ در هر ماژول میتواند کل برنامه را مختل کند.

و اما مزایای سیستم‌های یکپارچه:

  1. توسعه‌ی آن نسبت به میکروسرویس‌ها ساده است.
  2. انتشار آن آسان‌تر است؛ زیرا فقط یک خروجی، مستقر شده‌است.
  3. در مقایسه با معماری میکروسرویس‌ها، توسعه‌ی آن نسبتا آسان‌تر و ساده‌تر است.
  4. مشکلات تأخیر و امنیت شبکه در مقایسه با معماری میکروسرویس‌ها نسبتاً کمتر است.
  5. توسعه دهندگان نیازی به یادگیری برنامه‌های مختلف ندارند؛ آنها می‌توانند تمرکز خود را بر روی یک برنامه حفظ کنند.


میکروسرویس ها

این یک سبک توسعه معماری است که در آن برنامه از سرویس‌های کوچکتری تشکیل شده‌است که بخش کوچکی از عملکرد و داده‌ها را با برقراری ارتباط مستقیم با یکدیگر، با استفاده از پروتکلی مانند HTTP مدیریت می‌کند. به عبارتی دیگر خدمات یا سرویس‌های کوچکی هستند که با هم کار می‌کنند.

معماری میکروسرویس تأثیر بسزایی در رابطه‌ی بین برنامه و پایگاه داده دارد. بجای اشتراک گذاری یک پایگاه داده با سایر میکروسرویس‌ها، هر میکروسرویس، پایگاه داده خاص خود را دارد که اغلب منجر به تکثیر برخی از داده‌ها می‌شود، اما اگر می‌خواهید از این معماری بهره مند شوید، داشتن یک پایگاه داده در هر میکروسرویس، ضروری است؛ زیرا اتصال ضعیف را تضمین می‌کند. مزیت دیگر داشتن یک پایگاه داده‌ی مجزا برای هر میکروسرویس این است که هر میکروسرویس می‌تواند از نوع پایگاه داده‌ای که برای نیازهای خود مناسب‌تر است، استفاده کند. هر سرویس یک ماژول را ارائه می‌دهد، به طوری که خدمات مختلف را می‌توان به زبان‌های برنامه نویسی مختلف نوشت. الگوهای زیادی در معماری میکروسرویس دخیل هستند مانند discovery و registry service ، Caching ، ارتباط API ، امنیت و غیره.



اصول میکروسرویس‌ها:

تک مسئولیتی: یکی از اصولی است که به عنوان بخشی از الگوی طراحی SOLID تعریف شده است. بیان می‌کند که یک Unit ، یا یک کلاس، یک متد یا یک میکروسرویس باید تنها یک مسئولیت را داشته باشد. هر میکروسرویس باید یک مسئولیت داشته باشد و یک عملکرد واحد را ارائه دهد. شما همچنین می‌توانید بگویید تعداد میکروسرویس‌هایی که باید توسعه دهید، برابر با تعداد عملکردهای مورد نیاز شما است. پایگاه داده نیز غیرمتمرکز است و به طور کلی، هر میکروسرویس، پایگاه داده خاص خود را دارد.

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

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

مزایای میکروسرویس ها:

  1. مدیریت آن آسان است زیرا نسبتا کوچکتر است.
  2. اگر در یکی از میکروسرویس‌ها، بروزرسانی وجود داشته باشد، باید فقط آن میکروسرویس را مجدداً منتشر کنیم.
  3. میکروسرویس‌ها مستقل هستند و از این رو به طور مستقل منتشر می‌شوند. زمان راه اندازی و انتشار آنها نسبتاً کمتر است.
  4. برای یک توسعه‌دهنده جدید بسیار آسان است که وارد پروژه شود، زیرا او باید فقط یک میکروسرویس خاص را که عملکردی را که روی آن کار می‌کند، درک کند و نه کل سیستم را.
  5. اگر یک میکروسرویس خاص به دلیل استفاده بیش از حد کاربران از آن عملکرد، با بار زیادی مواجه است، ما باید فقط آن میکروسرویس را تنظیم کنیم. از این رو، معماری میکروسرویس از مقیاس بندی افقی پشتیبانی می‌کند.
  6. هر میکروسرویس بر اساس نیازهای تجاری می‌تواند از فناوری‌های مختلفی استفاده کند.
  7. اگر یک میکروسرویس خاص به دلیل برخی باگ‌ها از کار بیفتد، بر روی سایر میکروسرویس‌ها تأثیر نمی‌گذارد و کل سیستم دست نخورده باقی می‌ماند و به ارائه سایر عملکردها به کاربران ادامه می‌دهد.

معایب میکروسرویس ها:

  1. پیچیده است و پیچیدگی آن با افزایش تعداد ریز سرویس‌ها افزایش می‌یابد.
  2. نیاز به نیروهای متخصص
  3. استقرار مستقل میکروسرویس‌ها پیچیده‌است.
  4. میکروسرویس‌ها از نظر استفاده از شبکه پرهزینه هستند؛ زیرا نیاز به تعامل با یکدیگر دارند و همه این تماس‌های راه دور منجر به تأخیر شبکه می‌شود.
  5. امنیت کمتر به دلیل ارتباط بین سرویس‌ها
  6. اشکال زدایی دشوار است

مطالب
چگونه در یک پروژه سورس باز مشارکت کنیم؟
مشارکت در پروژه‌های سورس باز الزاما به معنای هدیه کدهای جدیدی به آن پروژه یا حتی مشارکت مالی در آن نیست. در ادامه لیستی از مواردی را مرور خواهیم کرد که سبب زنده نگه داشته شدن یک پروژه سورس باز خواهند شد:

مشارکت در نگهداری پروژه
  • مشکلی را در این کتابخانه پیدا کرده‌اید؟ آن‌را در سیستم bug tracking پروژه گزارش کنید و بی‌تفاوت از کنار آن عبور نکنید.
  • مشکلی برطرف شده است؟ بررسی کنید، آیا واقعا این تغییرات مفید بوده است یا خیر و نتیجه را اعلام کنید.

بهبود کدهای موجود
  • در بهترین حالت، کدی را جهت رفع یک مشکل ارسال کنید. همچنین در این حالت سعی کنید یک مطلب جدید را ایجاد کرده و در مورد کدهای خود توضیح دهید. برای ارسال کدی جدید بهتر است تنها قسمت‌های تغییر کرده را ارسال کنید و اصطلاحا باید بتوانید توسط قابلیت‌های سورس کنترل‌ها یک patch را تهیه نمائید.
  • یک unit test جدید را به پروژه اضافه کنید. یک مثال جدید را برای قسمتی خاص تهیه نمائید.
  • ثوابت برنامه را به زبان‌های دیگر ترجمه کنید.
  • یک گزینه و قابلیت جدید را درخواست دهید.

بهبود مستندات پروژه
  • اگر توضیحات قسمت‌های مختلف و commentهای ارائه شده به نظر شما کافی نیست؛ آن‌ها را تکمیل کرده و یک patch برای آن ارائه دهید.
  • مستندات موجود را تکمیل کنید یا بهبود ببخشید.
  • یک مقاله‌ی جدید در مورد نحوه‌ی استفاده از آن پروژه بنویسید.
  • یک ویدیوی ساده آموزشی را در مورد آن تهیه کنید.

مشارکت در انجمن‌ها و شبکه‌های اجتماعی
  • به لیست سؤالات مطرح شده در یک پروژه مراجعه کرده و در آن مشارکت کنید. سعی کنید حضور مثبتی داشته باشید.
  • به دیگران در مورد وجود این پروژه اطلاع رسانی کنید.
  • اگر پروژه مفیدی است، به دیگران بگوئید این پروژه چقدر بر روی کار شما تاثیر داشته است و چه برنامه‌هایی را از طریق آن پیش برده‌اید.
 
مطالب
مقایسه ساختاری دو دیتابیس SQL Server

یکی از مواردی که در محیط کاری زیاد پیش می‌آید بحث همگام نبودن دیتابیس توسعه با دیتابیس کاری است.
منظور از دیتابیس توسعه، همان دیتابیسی است که برای برنامه نویسی و آزمایش از آن استفاده می‌شود و دیتابیس کاری هم مشخص است (برای مثال بر روی یک سرور در اینترانت داخلی یک شرکت و یا بر روی یک سرور اینترنتی قرار دارد). عادت‌های مختلفی هم این‌جا ممکن است وجود داشته باشد، برای مثال تغییرات جدید بر روی دیتابیس کاری اعمال شود و سپس فراموش شود که همان‌ها نیز باید به دیتابیس توسعه هم اعمال شوند تا در تغییرات بعدی برای آزمایش دچار مشکل نشویم و برعکس. بعد از یک مدت هم تبدیل به کابوس می‌شود؛ نمی‌دانیم الان دیتابیس کاری جدیدتر است یا دیتابیس توسعه؛ و یا اینکه کلا دو دیتابیس مفروض چه تفاوت‌های ساختاری با هم دارند (بدیهی است بحث دیتا در اینجا در درجه‌ی اول اهمیت قرار ندارد). فرصت این هم وجود ندارد که تک تک جداول، ویووها، رویه‌های ذخیره شده و خلاصه تمامی اشیاء مرتبط را بررسی کنیم که چه اختلافی با هم دارند. اینجا مستندات هم کمکی نخواهند کرد چون صحبت از یک جدول با 5 فیلد در میان نیست که موارد را سریع و به صورت دستی تطابق دهیم. همچنین این مشکل عموما زمانی رخ می‌دهد که یکی از دو طرف در حال حاضر مستندات کامل و به روزی ندارد. اکنون چه باید کرد؟
اولین فکری که به ذهن خطور می‌کند مراجعه به ابزارهای جانبی است (مثلا Red Gate's SQL Compare چند صد دلاری) غافل از اینکه خود Visual studio 2008 (نگارش‌های تیمی و دیتابیسی) این قابلیت را نیز ارائه می‌دهد (شکل زیر).


پس از انتخاب new schema comparison ، در صفحه‌ای که ظاهر می‌شود، بر روی new connection کلیک کرده و دیتابیس‌های مبداء و مقصد را جهت مقایسه ساختاری انتخاب نمائید و سپس بر روی دکمه Ok کلیک کنید.


اگر اس کیوال سرور 2008 را نصب کرده باشید، با پیغام زیر روبرو خواهید شد:


برای رفع این مشکل باید بسته به روز رسانی زیر را نصب کرد تا این نگارش نیز پشتیبانی شود:

(برای نصب حتما باید SP1 مربوط به VS.Net 2008 پیشتر نصب شده باشد)

پس از کلیک بر روی دکمه Ok، کار آنالیز دو دیتابیس شروع شده و تفاوت‌ها گزارش داده می‌شوند:


همچنین جهت سهولت کار، اسکریپت T-SQL ایی را نیز به نام schema update script تولید می‌کند که با اجرای آن به سادگی کار به روز رسانی دیتابیس مقصد صورت خواهد گرفت.


در پایان یا می‌توان اسکریپت تولید شده را ذخیره کرد و در زمانی دلخواه اجرا و اعمال نمود و یا می‌توان بلافاصله بر روی دکمه write updates که در نوار ابزار ظاهر شده است کلیک کرد تا دیتابیس مقصد از لحاظ ساختاری با دیتابیس مبداء یکی شود.



مطالب
ارتقاء به ASP.NET Core 1.0 - قسمت 9 - بررسی تغییرات مسیریابی
فعال سازی تنظیمات مسیریابی

یکی دیگر از تغییرات عمده‌ی ASP.NET Core با نگارش‌های قبلی آن، نحوه‌ی مدیریت مسیریابی‌های سیستم است. در نگارش‌های قبلی مبتنی بر HTTP Moduleها، مسیریابی‌ها توسط یک HTTP Module مخصوص، با pipeline اصلی ASP.NET یکپارچه شده‌اند و زمانیکه مسیر درخواستی با تنظیمات سیستم تطابق داشته باشد، پردازش کار به HTTP Handler مخصوص ASP.NET MVC منتقل می‌شود:


اما در ASP.NET Core مبتنی بر میان افزارها، زیر ساخت مسیریابی به صورت زیر تغییر کرده‌است:


میان افزار ASP.NET MVC را که در قسمت قبل فعال کردیم، باید بتواند کنترلر و اکشن متد متناظر با URL درخواستی را مشخص کند. این تصمیم گیری نیز بر اساس تنظیماتی به نام Routing انجام می‌شود. در قسمت قبل، حالت ساده و پیش فرض این تنظیمات را مورد استفاده قرار دادیم
 app.UseMvcWithDefaultRoute();
که مطابق سورس ASP.NET Core، معادل است با فراخوانی متد app.UseMvc، با قالب پیش فرضی به صورت زیر:
    public static IApplicationBuilder UseMvcWithDefaultRoute(this IApplicationBuilder app)
    {
      if (app == null)
        throw new ArgumentNullException("app");
      return app.UseMvc((Action<IRouteBuilder>) (routes => routes.MapRoute("default", "{controller=Home}/{action=Index}/{id?}")));
    }
قالب مشخص شده‌ی در اینجا به ASP.NET MVC می‌گوید که از کدام قسمت‌های URL باید نام کلاس کنترلر (کلاس ویژه‌ای که به کلمه‌ی Controller ختم می‌شود) و نام اکشن متد متناظر با آن‌را انتخاب کند (اکشن متد، متدی است عمومی در آن کلاس).
روش دیگر معرفی این تنظیمات، استفاده از Attribute routing است:
 [Route("[controller]/[action]")]


مسیریابی‌های قراردادی

در قسمت قبل، یک POCO Controller را به صورت ذیل تعریف کردیم و این کنترلر، بدون تعریف هیچ نوع مسیریابی خاصی در دسترس بود:
namespace Core1RtmEmptyTest.Controllers
{
  public class HomeController
  {
   public string Index()
   {
    return "Running a POCO controller!";
   }
  }
}
علت کار کردن مسیریابی آن نیز به ذکر متد app.UseMvcWithDefaultRoute در کلاس آغازین برنامه بر می‌گردد و همانطور که عنوان شد، این فراخوانی را می‌توان با فراخوانی واضح‌تر ذیل جایگزین کرد:
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
});
پارامتر این متد که جایگزین متد ConfigureRoutes، در نگارش‌های قبلی ASP.NET MVC شده‌است، از نوع IRouteBuilder می‌باشد.
در این تعاریف، هر کدام از قسمت‌های قرارگرفته‌ی داخل {}، مشخص کننده‌ی قسمتی از URL دریافتی بوده و نام‌های controller و action در اینجا جزو نام‌های از پیش مشخص شده هستند و برای نگاشت اطلاعات مورد استفاده قرار می‌گیرند. برای مثال اگر آدرس home/index/ درخواست شد، برنامه به کلاس HomeController و متد عمومی Index آن هدایت می‌شود. همچنین قسمت آخر این پردازش به ?id ختم شده‌است. وجود  ?، به معنای اختیاری بودن این پارامتر است و اگر در URL ذکر شود، به پارامتر id این اکشن متد، نگاشت خواهد شد. مواردی که پس از = ذکر شده‌اند، مقادیر پیش فرض مسیریابی هستند. برای مثال اگر صرفا آدرس home/ درخواست شود، مقدار اکشن متد آن با مقدار پیش فرض index جایگزین خواهد شد و اگر تنها مسیر / درخواست شود، کنترل Home و اکشن متد Index آن پردازش می‌شوند.
در اینجا به هر تعدادی که نیاز است می‌توان متدهای routes.MapRoute را فراخوانی و استفاده کرد؛ اما ترتیب تعریف آن‌ها حائز اهمیت است. هر مسیریابی که در ابتدای لیست اضافه شود، حق تقدم بالاتری خواهد داشت و هر تطابقی با یکی از مسیریابی‌های تعریف شده، در همان سطح سبب خاتمه‌ی پردازش سایر مسیریابی‌ها می‌شود.


استفاده از Attributes برای تعریف مسیریابی‌ها

بجای تعریف قرار دادهای پیش فرض مسیریابی در کلاس آغازین برنامه، می‌توان از ویژگی Route نیز استفاده کرد. هرچند روش تعریف مسیریابی‌های قراردادی، از نگارش‌های آغازین ASP.NET MVC به همراه آن بوده‌اند، اما با زیاد شدن تعداد کنترلرها و مسیریابی‌های سفارشی هر کدام، اینبار با نگاه کردن به یک کنترلر، سریع نمی‌توان تشخیص داد که چه مسیریابی‌های خاصی به آن مرتبط هستند. برای ساده سازی مدیریت برنامه‌های بزرگ و ساده سازی تعاریف مسیریابی‌های خاص آن‌ها، استفاده از ویژگی Route نیز به ASP.NET MVC اضافه شده‌است.
یک مثال: کنترلر About را درنظر بگیرید:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  public class AboutController : Controller
  {
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
این کلاس و کنترلر، به صورت پیش فرض نیاز به تعریف هیچ نوع مسیریابی جدیدی ندارد. همان مسیریابی پیش فرض ثبت شده‌ی در کلاس آغازین برنامه، تمام متدهای عمومی آن یا همان اکشن متدهای آن‌را پوشش می‌دهد. برای مثال جهت رسیدن به اکشن متد SiteName آن، می‌توان آدرس /About/SiteName/ را درخواست داد.
اما اگر آدرس /About/ را درخواست دهیم چطور؟ چون در مسیریابی پیش فرض، تعریف {action=Index} را داریم، یعنی هر زمانیکه در URL درخواستی، قسمت action آن ذکر نشد، آن‌را با index جایگزین کن و این کنترلر دارای متد Index نیست. در ادامه اگر بخواهیم متد Hello را تبدیل به متد پیش فرض این کنترلر کنیم، می‌توان با استفاده از ویژگی Route به صورت ذیل عمل کرد:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  [Route("About")]
  public class AboutController : Controller
  {
   [Route("")]
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   [Route("SiteName")]
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
در اینجا با اولین Route تعریف شده، مشخص کرده‌ایم که اگر قسمت اول URL درخواستی معادل about بود، پردازش برنامه باید به این کنترلر هدایت شود. بدیهی است الزامی به یکی بودن نام Route، با نام کنترلر، وجود ندارد. همچنین Route تعریف شده‌ی با رشته‌ی خالی، به معنای مسیریابی پیش فرض است. یعنی اگر آدرس /about/ درخواست داده شد، اکشن متد پیش فرض آن، متد Hello خواهد بود. در این حالت، ذکر Route بعدی برای اکشن متد SiteName الزامی است و اگر این‌کار صورت نگیرد، به استثنای ذیل خواهیم رسید:
 AmbiguousActionException: Multiple actions matched. The following actions matched route data and had all constraints satisfied:

Core1RtmEmptyTest.Controllers.AboutController.Hello (Core1RtmEmptyTest)
Core1RtmEmptyTest.Controllers.AboutController.SiteName (Core1RtmEmptyTest)
که عنوان کرده‌است در این حالت مشخص نیست که اکشن متد پیش فرض، کدام است.

روش بهتر و refactoring friendly آن نیز به صورت ذیل است:
using Microsoft.AspNetCore.Mvc;
 
namespace Core1RtmEmptyTest.Controllers
{
  [Route("[controller]")]
  public class AboutController : Controller
  {
   [Route("")]
   public ActionResult Hello()
   {
    return Content("Hello from DNT!");
   }
 
   [Route("[action]")]
   public ActionResult SiteName()
   {
    return Content("DNT");
   }
  }
}
عموما مرسوم است که نام مسیریابی کنترلر همان نام کنترلر باشد و نام مسیریابی اکشن متد، همان نام اکشن متد مربوطه. به همین جهت می‌توان از توکن‌های ویژه‌ی [controller] و [action] نیز در اینجا استفاده کرد که دقیقا به همان نام کنترلر و اکشن متد متناظر با آن‌ها تفسیر خواهند شد. مزیت این‌کار این است که در صورت تغییر نام متدها یا کنترلرها، دیگر نیازی نیست تا نام‌های تعریف شده‌ی در ویژگی‌های Route را نیز تغییر داد.

یک نکته: در حین تعریف مسیریابی یک کنترلر می‌توان پیشوندهایی را نیز ذکر کرد؛ برای مثال:
 [Route("api/[controller]")]
وجود api در اینجا به این معنا است که از این پس تنها آدرس /api/about/ پردازش خواهد شد و اگر صرفا آدرس /about/ درخواست شود، با خطای 404 و یا یافت نشد، کار خاتمه می‌یابد.


تعریف قیود، برای مسیریابی‌های تعریف شده

فرض کنید به کنترلر About فوق، اکشن متد ذیل را که یک خروجی JSON را بازگشت می‌دهد، اضافه کرده‌ایم:
//[Route("/Users/{userid}")]
[Route("Users/{userid}")]
public IActionResult GetUsers(int userId)
{
    return Json(new { userId = userId });
}
در اینجا تعریف مسیریابی آن با users/ و user معانی کاملا متفاوتی را دارند. اگر مسیریابی Users/{userid}/ را تعریف کنیم، یعنی مسیر ذیل از ریشه‌ی سایت باید درخواست شود: http://localhost:7742/users/1
و اگر مسیریابی Users/{userid} را تعریف کنیم، یعنی این مسیریابی پس از ذکر کنترلر about، به عنوان یک اکشن متد آن مفهوم پیدا می‌کند:
http://localhost:7742/about/users/1
در هر دو حالت، ذکر پارامتر userid الزامی است (چون با ? مشخص نشده‌است)؛ مانند:
[Route("/Users/{userid:int?}")]
در اینجا اگر بخواهیم نوع پارامتر درخواستی را نیز دقیقا مشخص و مقید کنیم، می‌توان به صورت ذیل عمل کرد:
 [Route("Users/{userid:int}")]
اگر این کار را انجام ندهیم، با درخواست مسیر http://localhost:7742/dnt/about/users/test مقدار صفر به userId ارسال می‌شود (چون پارامتر test عددی نیست). اگر تنظیم فوق را انجام دهیم، کاربر خطای 404 را دریافت می‌کند.

قیودی را که در اینجا می‌توان ذکر کرد به شرح زیر هستند:
• alpha - معادل است با  (a-z, A-Z).
• bool - برای تطابق با مقادیر بولی.
• datetime - برای تطابق با تاریخ میلادی.
• decimal - برای تطابق با ورودی‌های اعشاری.
• double - برای تطابق با اعداد اعشاری 64 بیتی.
• float - برای تطابق با اعداد اعشاری 32 بیتی.
• guid - برای تطابق با GUID ها
• int - برای تطابق با اعداد صحیح 32 بیتی.
• length - برای تعیین طول رشته.
• long - برای تطابق با اعداد صحیح 64 بیتی.
• max - برای ذکر حداکثر مقدار یک عدد صحیح.
• maxlength - جهت ذکر حداکثر طول رشته‌ی مجاز ورودی.
• min - برای ذکر حداقل مقدار یک عدد صحیح.
• minlength - جهت ذکر حداقل طول رشته‌ی مجاز ورودی.
• range - ذکر بازه‌ی اعداد صحیح مجاز.
• regex - ذکر یک عبارت با قاعده جهت مشخص سازی الگوی قابل پذیرش.

برای ترکیب چندین قید مختلف نیز می‌توان از : استفاده کرد:
 [Route("/Users/{userid:int:max(1000):min(10)}")]


ذکر نام Route برای ساده سازی تعریف آدرسی به آن

در حین تعریف یک Route می‌توان نام دلخواهی را نیز به آن انتساب داد (همانند نام default مسیریابی ثبت شده‌ی در کلاس آغازین برنامه):
 [Route("/Users/{userid:int}", Name="GetUserById")]
مزیت آن این است که اکنون برای اشاره‌ی به این مسیریابی خاص می‌توان از این نام تعریف شده استفاده کرد:
 string uri = Url.Link("GetUserById", new { userid = 1 });
پارامتر اول ذکر شده، نام مسیریابی و پارامتر دوم، پارامترهای مرتبط با این مسیریابی هستند.


مشخص سازی ترتیب پردازش مسیریابی‌ها

ترتیب مسیریابی‌های ثبت شده‌ی در کلاس آغازین برنامه، همان ترتیب افزوده شدن و ذکر آن‌ها است.
در اینجا می‌توان از خاصیت order نیز استفاده کرد و اعداد کوچکتر، ابتدا پردازش می‌شوند (مقدار پیش فرض آن نیز صفر است):
 [Route("/Users/{userid:int}", Name = "GetUserById", Order = 1)]


امکان تعریف قیود سفارشی

اگر قیودی که تا اینجا ذکر شدند، برای کار شما مناسب نبودند و نیاز بود تا الگوریتم خاصی را جهت محدود سازی دسترسی به یک مسیریابی خاص پیاده سازی کنید، می‌توان به صورت ذیل عمل کرد:
using System;
using System.Globalization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
 
namespace Core1RtmEmptyTest
{
  public class CustomRouteConstraint : IRouteConstraint
  {
   public bool Match(HttpContext httpContext, IRouter route, string routeKey, RouteValueDictionary values,
    RouteDirection routeDirection)
   {
    object value;
    if (!values.TryGetValue(routeKey, out value) || value == null)
    {
      return false;
    }
 
    long longValue;
    if (value is long)
    {
      longValue = (long)value;
      return longValue != 10;
    }
 
    var valueString = Convert.ToString(value, CultureInfo.InvariantCulture);
    if (long.TryParse(valueString, NumberStyles.Integer,
      CultureInfo.InvariantCulture, out longValue))
    {
      return longValue != 10;
    }
    return false;
   }
  }
}
در اینجا یک کلاس جدید را که اینترفیس IRouteConstraint را پیاده سازی می‌کند تعریف کرده‌ایم:
public class CustomRouteConstraint : IRouteConstraint
سپس در متد match آن بررسی کرده‌ایم که اگر userid=10 بود، خطای 404 صادر شود.
در آخر برای ثبت و معرفی آن باید به متد ConfigureServices کلاس آغازین برنامه مراجعه کرد:
public void ConfigureServices(IServiceCollection services)
{
    services.AddRouting(options =>options.ConstraintMap.Add("Custom", typeof(CustomRouteConstraint)));
پس از آن، این نام جدید ثبت شده‌ی در اینجا، به نحو ذیل قابل استفاده است:
 [Route("/Users/{userid:int:custom}")]
به این ترتیب userid باید از نوع int بوده و همچنین قید custom را نیز پوشش دهد (یعنی userid=10 نباشد).

یک نکته:  اگر به سورس ASP.NET Core مراجعه کنید ، تمام قیودی را که پیشتر نام بردیم (مانند int، guid و امثال آن) نیز به همین روش تعریف و پیشتر ثبت شده‌اند.


معرفی بسته‌ی نیوگت Microsoft.AspNetCore.SpaServices

مسیریابی‌های پیش فرض ASP.NET Core با مسیریابی‌های برنامه‌های SPA مانند AngularJS (و امثال آن) تداخل دارند؛ از این جهت که درخواست‌های رسیده‌ی به سرور، ابتدا به موتور پردازشی ASP.NET وارد می‌شوند و اگر یافت نشدند، کاربر با پیام 404 مواجه خواهد شد و دیگر در اینجا برنامه به مسیریابی خاص مثلا AngularJS 2.0 هدایت نمی‌شود.
برای این موارد مرسوم است که یک fallback route را در انتهای مسیریابی‌های موجود اضافه کنند (به آن catch all هم می‌گویند)
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
 
  routes.MapRoute(
   name: "spa-fallback",
   template: "{*url}",
   defaults: new { controller = "Home", action = "Index" });
});
در اینجا هر درخواستی که با مسیریابی default تطابق نداشت، توسط الگوی عمومی {url*} پردازش می‌شود و این پردازش در نهایت سبب راه اندازی برنامه‌ی SPA می‌گردد. اما مشکل اینجا است که برای فایل‌های استاتیک غیرموجود مانند تصاویر، فایل‌های js و css نیز خروجی HTML ایی خواهیم داشت؛ بجای خروجی 404 و یافت نشد.
برای حل این مشکل مایکروسافت بسته‌ای را به نام Microsoft.AspNetCore.SpaServices ارائه داده است.
برای افزودن آن بر روی گره references کلیک راست کرده و گزینه‌ی manage nuget packages را انتخاب کنید. سپس در برگه‌ی browse آن Microsoft.AspNetCore.SpaServices را جستجو کرده و نصب نمائید:


انجام این مراحل معادل هستند با افزودن یک سطر ذیل به فایل project.json برنامه:
{
    "dependencies": {
       //same as before  
       "Microsoft.AspNetCore.SpaServices": "1.0.0-beta-000007"
 },
پس از بازیابی و نصب آن، اکنون catch all را حذف کرده و با یک سطر routes.MapSpaFallbackRoute ذیل جایگزین کنید:
app.UseMvc(routes =>
{
  routes.MapRoute(
   name: "default",
   template: "{controller=Home}/{action=Index}/{id?}");
 
  routes.MapSpaFallbackRoute("spa-fallback", new { controller = "Home", action = "Index" });
});
و برای یادآوری مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 4 - فعال سازی پردازش فایل‌های استاتیک» در AngularJS 2.0، علاوه بر عمومی کردن پوشه‌ی wwwroot توسط UseFileServer نیاز است پوشه‌ی node_modules را هم با تنظیمات ذیل عمومی کرد و در معرض دید عموم قرار داد (جایی که بسته‌های node.js نصب می‌شوند):
// Serve wwwroot as root
app.UseFileServer();
 
// Serve /node_modules as a separate root (for packages that use other npm modules client side)
app.UseFileServer(new FileServerOptions
{
  // Set root of file server
  FileProvider = new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "node_modules")),
  // Only react to requests that match this path
  RequestPath = "/node_modules",
  // Don't expose file system
  EnableDirectoryBrowsing = false
});
مطالب
رده‌ها و انواع مختلف بانک‌های اطلاعاتی NoSQL
4 رده و گروه عمده بانک‌های اطلاعاتی NoSQL وجود دارند؛ شامل:
الف) Key-Value stores که پایه بانک‌های اطلاعاتی NoSQL را تشکیل داده و اهدافی عمومی را دنبال می‌کنند.
ب) Wide column stores که در شرکت‌های بزرگ اینترنتی بیشتر مورد استفاده قرار گرفته‌اند.
ج) Document stores یا بانک‌های اطلاعاتی NoSQL سندگرا.
د) Graph databases که بیشتر برای ردیابی ارتباطات بین موجودیت‌ها بکار می‌روند.

و در تمام این گروه‌ها، مکانیزم‌های Key-Value به شدت مورد استفاده‌اند.


الف) Key-Value stores
Key-Value stores یکی از عمومی‌ترین و پایه‌ای‌ترین گروه‌های بانک‌های اطلاعاتی NoSQL را تشکیل می‌دهند. البته این مورد بدین معنا نیست که این رده، جزو محبوب‌ترین‌ها نیز به‌شمار می‌روند.


این نوع بانک‌های اطلاعاتی شامل جداولی از اطلاعات هستند. هر جدول نیز شامل تعدادی ردیف است؛ چیزی همانند بانک‌های اطلاعاتی رابطه‌ای. اما در هر ردیف، یک Dictionary یا آرایه‌ای از اطلاعات key-value شکل را شاهد خواهید بود. در اینجا ساختار و اسکیمای ردیف‌ها می‌توانند نسبت به یکدیگر کاملا متفاوت باشند (دید لیبرال نسبت به اسکیما، که در قسمت قبل به آن پرداخته شد). در این بین، تنها تضمین خواهد شد که هر ردیف، Id منحصربفردی دارد.
از این نوع بانک‌های اطلاعاتی، در سکوهای کاری ابری زیاد استفاده می‌شود. دو مثال مهم در اینباره شامل Amazon SimpleDB و Azure Table Storage هستند.
سایر نمونه‌های مهم دیگری از بانک‌های اطلاعاتی NoSQL که بر مبنای مفهوم Key-Value stores کار می‌کنند، عبارتند از MemcacheDB و Voldemort. به علاوه در Amazon web services بانک اطلاعاتی دیگری به نام DynamoDB به عنوان یک سرویس عمومی در دسترس است. همچنین Dynomite نیز به عنوان نمونه سورس باز Dynamo مطرح است.
Redis و Riak نیز جزو بانک‌های اطلاعاتی Key-Value store بسیار معروف به‌شمار می‌روند.

همانطور که در تصویر فوق ملاحظه می‌کنید، Key-Value stores دارای بانک‌های اطلاعاتی شامل جداول مختلف هستند. در اینجا همچنین ساختار ردیف‌هایی از اطلاعات این جداول نیز مشخص شده‌اند. هر ردیف، یک کلید دارد به همراه تعدادی جفت کلید-مقدار. در این جداول، اسکیما ثابت نگه داشته شده است و از ردیفی به ردیف دیگر متفاوت نیست؛ اما این مساله اختیاری است. برای مثال می‌توان در ردیف اطلاعات یک مشتری خاص، کلید-مقدارهایی خاص او را نیز درج کرد که لزوما در سایر ردیف‌ها، نیازی به وجود آن‌ها نیست.
به علاوه باید به خاطر داشت که هرچند به ظاهر last_orderها به شماره Id سفارشات مرتبط هستند، اما مفاهیمی مانند کلیدهای خارجی بانک‌های اطلاعاتی رابطه‌ای، در اینجا وجود خارجی ندارند. بیشتر در اینجا هدف سهولت جستجوی اطلاعات است.


ب) Wide column stores
Wide column stores دارای جداولی است که درون آن‌ها ستون‌هایی قابل تعریف است. درون این ستون‌ها که یادآور بانک‌های اطلاعاتی رابطه‌ای هستند، اطلاعات به شکل key-value با ساختاری متفاوت، قابل ذخیره سازی هستند. در اینجا هر ستون، می‌تواند شامل گروهی از ستون‌ها که بر اساس مفاهیم جفت‌های key-value کار می‌کنند، باشد.
این نوع بانک‌های اطلاعاتی عموما در سایت‌های اینترنتی بسیار بزرگ و برنامه‌های «Big data» استفاده می‌شوند. برای مثال:


- BigTable گوگل که یک محصول اختصاصی و غیرعمومی است؛ اما جزئیات آن را به عنوان مقالات علمی منتشر کرده است.
- دنیای سورس باز به رهبری Yahoo، نمونه سورس باز BigTable را به نام Hbase ارائه داده است.
- در فیس بوک، از بانک اطلاعاتی دیگری به نام Cassandra استفاده می‌کنند. در اینجا به گروهی از ستون‌ها super columns و جداول super column families گفته می‌شود.

در اینجا نیز جداول و ردیف‌ها وجود دارند و هر ستون باید عضوی از خانواده یک super column باشد. ساختار ردیف‌ها در این تصویر یکسان درنظر گرفته شده‌اند، اما اگر نیاز بود، برای مثال می‌توان در ردیفی خاص، ساختار را تغییر داد و مثلا middle name را نیز بر اساس نیاز، به ردیفی اضافه کرد.


ج) Document stores
Document stores بجای جداول، دارای بانک‌های اطلاعاتی مختلفی هستند و در اینجا بجای ردیف‌ها، سند یا document دارند. ساختار سندها نیز عموما بر مبنای اشیاء JSON تعریف می‌گردد (که البته این مورد الزامی نبوده و از هر محصول، به محصول دیگری ممکن است متفاوت باشد؛ اما عمومیت دارد). بنابراین هر سند دارای تعدادی خاصیت است (چون اشیاء JSON به این نحو تعریف می‌گردند) که دارای مقدار هستند. در نگاه اول، شاید این نوع اسناد، بسیار شبیه به key-value stores به نظر برسند. اما در حین تعریف اشیاء JSON، یک مقدار می‌تواند خود یک شیء کامل دیگر باشد و نه صرفا یک مقدار ساده. به همین جهت عده‌ای به این نوع بانک‌های اطلاعاتی، بانک‌های اطلاعاتی Key-value store سفارشی و خاص نیز می‌گویند.
این نوع ساختار منعطف، برای ذخیره سازی اطلاعات اشیاء تو در تو و درختی بسیار مناسب است. همچنین این اسناد می‌توانند حاوی پیوست‌هایی نیز باشد؛ مانند پیوست یک فایل به یک سند.
در Document stores، نگارش‌های قدیمی اسناد نیز نگهداری می‌گردند. به همین جهت این نوع بانک‌های اطلاعاتی برای ایجاد برنامه‌های مدیریت محتوا نیز بسیار مطلوب می‌باشند.
با توجه به مزایایی که برای این رده از بانک‌های اطلاعاتی NoSQL ذکر گردید، Document stores در بین برنامه نویس‌ها بسیار محبوب و پرکاربرد هستند.
از این دست بانک‌های اطلاعاتی NoSQL، می‌توان به CouchDB ، MongoDB و RavenDB اشاره کرد.
سایر مزایای Document stores که به پرکاربرد شدن آن‌ها کمک کرده‌اند به شرح زیر هستند:
- هر سند را می‌توان با یک URI آدرس دهی کرد.
- برای نمونه CouchDB از یک full REST interface برای دسترسی و کار با اسناد پشتیبانی می‌کند (چیزی شبیه به ASP.NET WEB API در دات نت). در اینجا با استفاده از یک وب سرور توکار و بکارگیری HTTP Verbs مانند Put، Delete، Get و غیره، امکان کار با اسناد وجود دارد.
- اغلب بانک‌های اطلاعاتی Document stores از JavaScript به عنوان native language خود بهره می‌برند (جهت سهولت کار با اشیاء JSON).


در اینجا دو دیتابیس، بجای دو جدول وجود دارند. همچنین در مقایسه با بانک‌های اطلاعاتی key-value، برای نمونه، مقدار خاصیت آدرس، خود یک شیء است که از دو خاصیت تشکیل شده است. به علاوه هر خاصیت Most_Recent یک Order، به سند دیگری در بانک اطلاعاتی Orders لینک شده است.


د) Graph databases
Graph databases نوع خاصی از بانک‌های اطلاعاتی NoSQL هستند که جهت ردیابی ارتباطات بین اطلاعات طراحی شده‌اند و برای برنامه‌های شبکه‌های اجتماعی بسیار مفید هستند.
در واژه نامه این بانک‌های اطلاعاتی Nodes و Edges (اتصال دهنده‌های نودها) تعریف شده‌اند. در اینجا نودها می‌توانند دارای خاصیت‌ها و مقادیر متناظر با آن‌ها باشند.
یکی از معروفترین Graph databases مورد استفاده، Neo4j نام دارد.


در اینجا یک شخص را که دارای رابطه آدرس با شیء آدرس ذکر شده است را مشاهده می‌کنید. همچنین این شخص دارای رابطه دوستی با سه شخص دیگر است.
مطالب
بررسی تغییرات Blazor 8x - قسمت دهم - مدیریت حالت کاربران در روش‌های مختلف رندر
رفتار Blazorهای پیش از دات‌نت 8 در مورد مدیریت حالت

پیش از دات نت 8، دو حالت عمده برای توسعه‌ی برنامه‌های Blazor وجود داشت: Blazor Server و Blazor WASM. در هر دو حالت، طول عمر سیستم تزریق وابستگی‌های ایجاد و مدیریت شده‌ی توسط Blazor، معادل طول عمر برنامه‌است.

در برنامه‌های Blazor Server، طول عمر سیستم تزریق وابستگی‌ها، توسط ASP.NET Core قرار گرفته‌ی بر روی سرور مدیریت شده و نمونه‌های ایجاد شده‌ی سرویس‌های توسط آن، به ازای هر کاربر متفاوت است. بنابراین اگر طول عمر سرویسی در اینجا به صورت Scoped تعریف شود، این سرویس فقط یکبار در طول عمر برنامه، به ازای یک کاربر جاری برنامه، تولید و نمونه سازی می‌شود. در این مدل برنامه‌ها، سرویس‌هایی با طول عمر Singleton، بین تمام کاربران به اشتراک گذاشته می‌شوند. به همین جهت است که در این نوع برنامه‌ها، مدیریت سرویس Context مخصوص EF-Core‌ نکات خاصی را به همراه دارد. چون اگر بر اساس سیستم پیش‌فرض تزریق وابستگی‌ها و طول عمر Scoped این سرویس عمل شود، یک Context فقط یکبار به‌ازای یک کاربر، یکبار نمونه سازی شده و تا پایان طول عمر برنامه، بدون تغییر زنده نگه داشته می‌شود؛ در حالیکه عموم توسعه دهندگان EF-Core تصور می‌کنند سرویس‌های Scoped، پس از پایان یک درخواست، پایان یافته و Dispose می‌شوند، اما در اینجا پایان درخواستی نداریم. یک اتصال دائم SignalR را داریم و تا زمانیکه برقرار است، یعنی برنامه زنده‌است. بنابراین در برنامه‌های Blazor Server، سرویس‌های Scoped، به ازای هر کاربر، همانند Singleton رفتار می‌کنند (در سراسر برنامه به ازای یک کاربر در دسترس هستند) و سرویس‌هایی از اساس Singleton، بین تمام کاربران به اشتراک گذاشته می‌شوند.

در برنامه‌های Blazor WASM، طول عمر سیستم تزریق وابستگی‌ها، توسط برنامه‌ی وب‌اسمبلی در حال اجرای بر روی مرورگر مدیریت می‌شود. یعنی مختص به یک کاربر بوده و طول عمر آن وابسته‌است به طول عمر برگه‌ی جاری مرورگر. بنابراین دراینجا بین سرویس‌های Scoped و Singleton، تفاوتی وجود ندارد و همانند هم رفتار می‌کنند (هر دو مختص به یک کاربر و وابسته به طول عمر برگه‌ی جاری هستند).

در هیچکدام از این حالت‌ها، امکان دسترسی به HttpContext وجود ندارد (نه داخل اتصال دائم SignalR برنامه‌های Blazor Server و نه داخل برنامه‌ی وب‌اسمبلی در حال اجرای در مرورگر). اطلاعات بیشتر
بنابراین در این برنامه‌ها برای نگهداری اطلاعات کاربر لاگین شده‌ی به سیستم و یا سایر اطلاعات سراسری برنامه، عموما از سرویس‌هایی با طول عمر Scoped استفاده می‌شود که در تمام قسمت‌های برنامه به ازای هر کاربر، قابل دسترسی هستند.

 
رفتار Blazor 8x در مورد مدیریت حالت

هرچند دات نت 8 به همراه حالت‌های رندر جدیدی است، اما هنوز هم می‌توان برنامه‌هایی کاملا توسعه یافته بر اساس مدل‌های قبلی Blazor Server و یا Blazor WASM را همانند دات‌نت‌های پیش از 8 داشت. بنابراین اگر تصمیم گرفتید که بجای استفاده از جزیره‌های تعاملی، کل برنامه را به صورت سراسری تعاملی کنید، همان نکات قبلی، در اینجا هم صادق هستند و از لحاظ مدیریت حالت، تفاوتی نمی‌کنند.

اما ... اگر تصمیم گرفتید که از حالت‌های رندر جدید استفاده کنید، مدیریت حالت آن متفاوت است؛ برای مثال دیگر با یک سیستم مدیریت تزریق وابستگی‌ها که طول عمر آن با طول عمر برنامه‌ی Blazor یکی است، مواجه نیستیم و حالت‌های زیر برای آن‌ها متصور است:

حالت رندر: صفحات رندر شده‌ی در سمت سرور یا Server-rendered pages
مفهوم: یک صفحه‌ی Blazor که در سمت سرور رندر شده و HTML نهایی آن به سمت مرورگر کاربر ارسال می‌شود. در این حالت هیچ اتصال SignalR و یا برنامه‌ی وب‌اسمبلی اجرا نخواهد شد.
عواقب: طول عمر سرویس‌های Scoped، به‌محض پایان رندر صفحه در سمت سرور، پایان خواهند یافت.
بنابراین در این حالت طول عمر یک سرویس Scoped، بسیار کوتاه است (در حد ابتدا و انتهای رندر صفحه). همچنین چون برنامه در سمت سرور اجرا می‌شود، دسترسی کامل و بدون مشکلی را به HttpContext دارد.
صفحات SSR، بدون حالت (stateless) هستند؛ به این معنا که حالت کاربر در بین هدایت به صفحات مختلف برنامه ذخیره نمی‌شود. به آن‌ها می‌توان از این لحاظ به‌مانند برنامه‌های MVC/Razor pages نگاه کرد. در این حالت اگر می‌خواهید حالت کاربران را ذخیره کنید، استفاده از کوکی‌ها و یا سشن‌ها، راه‌حل متداول اینکار هستند.

حالت رندر: صفحات استریمی (Streamed pages)
مفهوم: یک صفحه‌ی Blazor که در سمت سرور رندر شده و قطعات آماده شده‌ی HTML آن به صورت استریمی از داده‌ها، به سمت مرورگر کاربر ارسال می‌شوند. در این حالت هیچ اتصال SignalR و یا برنامه‌ی وب‌اسمبلی اجرا نخواهد شد.
عواقب: طول عمر سرویس‌های Scoped، به‌محض پایان رندر صفحه در سمت سرور، پایان خواهند یافت.
بنابراین در این حالت طول عمر یک سرویس Scoped، بسیار کوتاه است (در حد ابتدا و انتهای رندر صفحه). همچنین چون برنامه در سمت سرور اجرا می‌شود، دسترسی کامل و بدون مشکلی را به HttpContext دارد.

حالت رندر: Blazor server page
مفهوم: یک صفحه‌ی Blazor Server که یک اتصال دائم SignalR را با سرور دارد.
عواقب: طول عمر سرویس‌های Scoped، معادل طول عمر اتصال SignalR است و با قطع این اتصال، پایان خواهند یافت. این نوع برنامه‌ها اصطلاحا stateful هستند و از لحاظ دسترسی به حالت کاربر، تجربه‌ی کاربری همانند یک برنامه‌ی دسکتاپ را ارائه می‌دهند.
در این نوع برنامه‌ها و درون اتصال SignalR، دسترسی به HttpContext وجود ندارد.

حالت رندر: Blazor wasm page
مفهوم: صفحه‌ای که به کمک فناوری وب‌اسمبلی، درون مرورگر کاربر اجرا می‌شود.
عواقب: طول عمر سرویس‌های Scoped، معادل طول عمر برگه و صفحه‌ی جاری است و با بسته شدن آن، پایان می‌پذیرد. این نوع برنامه‌ها نیز اصطلاحا stateful هستند و از لحاظ دسترسی به حالت کاربر، تجربه‌ی کاربری همانند یک برنامه‌ی دسکتاپ را ارائه می‌دهند (البته فقط درون مروگر کاربر).
در این نوع برنامه‌ها، دسترسی به HttpContext وجود ندارد.

حالت رندر: جزیره‌ی تعاملی Blazor Server و یا Blazor server island
مفهوم: یک کامپوننت Blazor Server که درون یک صفحه‌ی دیگر (که عموما از نوع SSR است) قرار گرفته و یک اتصال SignalR را با سرور برقرار می‌کند.
عواقب: طول عمر سرویس‌های Scoped، معادل طول عمر اتصال SignalR است و با قطع این اتصال، پایان خواهند یافت؛ برای مثال کاربر به صفحه‌ای دیگر در این برنامه مراجعه کند. بنابراین این نوع کامپوننت‌ها هم تا زمانیکه کاربر در صفحه‌ی جاری قرار دارد، stateful هستند.
در این نوع برنامه‌ها و درون اتصال SignalR، دسترسی به HttpContext وجود ندارد.

حالت رندر: جزیره‌ی تعاملی Blazor WASM و یا Blazor wasm island
مفهوم: یک کامپوننت Blazor WASM که درون یک صفحه‌ی دیگر (که عموما از نوع SSR است) توسط فناوری وب‌اسمبلی، درون مرورگر کاربر اجرا می‌شود.
عواقب: طول عمر سرویس‌های Scoped، معادل مدت زمان فعال بودن صفحه‌ی جاری است. به محض اینکه کاربر به صفحه‌ای دیگر مراجعه و این کامپوننت دیگر فعال نباشد، طول عمر آن خاتمه خواهد یافت. بنابراین این نوع کامپوننت‌ها هم تا زمانیکه کاربر در صفحه‌ی جاری قرار دارد، stateful هستند (البته این حالت درون مرورگر کاربر مدیریت می‌شود و نه در سمت سرور).
در این نوع برنامه‌ها، دسترسی به HttpContext وجود ندارد.


نتیجه‌گیری

همانطور که مشاهده می‌کنید، در صفحات SSR، دسترسی کاملی به HttpContext سمت سرور وجود دارد (که البته کوتاه مدت بوده و با پایان رندر صفحه، خاتمه خواهد یافت؛ حالتی مانند صفحات MVC و Razor pages)، اما در جزایر تعاملی واقع در آن‌ها، خیر.
مساله‌ی مهم در اینجا، مدیریت اختلاط حالت صفحات SSR و جزایر تعاملی واقع در آن‌ها است. مایکروسافت جهت پیاده سازی اعتبارسنجی و احراز هویت کاربران در Blazor 8x و برای انتقال حالت به این جزایر، از دو روش Root-level cascading values و سرویس PersistentComponentState استفاده کرده‌است که آن‌ها را در دو قسمت بعدی، با توضیحات بیشتری بررسی می‌کنیم.
مطالب
بهبود SEO در ASP.NET MVC
گوگل خلاصه نتایج Indexing یک سایت را توسط ابزاری به نام Google webmaster tools در اختیار علاقمندان قرار می‌دهد. Bing نیز چنین ابزاری را تدارک دیده است.
به آمارهای خطای حاصل از سایت جاری که دقت می‌کردم یک نکته آن جالب بود: «محتوای تکراری»


mydomain.com/Home/Index
mydomain.com/home/index
mydomain.com/Home/index
mydomain.com/home/Index
همانطور که ملاحظه می‌کنید، گوگل به کوچکی و بزرگی حروف بکار رفته در لینک‌ها حساس است. هرچند 4 لینک فوق به یک صفحه اشاره می‌کنند، اما گوگل 4 بار آن‌ها را ایندکس خواهد کرد و نهایتا به صورت یک خطای «محتوای تکراری» در گزارشات SEO آن ظاهر خواهد شد (به همراه کاهش رتبه SEO سایت).

راه حل

برای حل این مساله دو نکته باید درنظر گرفته شود:
الف) هدایت دائمی (Redirect permanent) صفحات قدیمی به صفحاتی جدید، با آدرس lowercase

using System.Globalization;
using System.Web;
using System.Web.Mvc;

namespace WebToolkit
{
    public class ForceWww : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            modifyUrlAndRedirectPermanent(filterContext);
            base.OnActionExecuting(filterContext);
        }

        private static void modifyUrlAndRedirectPermanent(ActionExecutingContext filterContext)
        {
            if (canIgnoreRequest(filterContext))
                return;

            var absoluteUrl = HttpUtility.UrlDecode(filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.ToString(CultureInfo.InvariantCulture));
            var absoluteUrlToLower = absoluteUrl.ToLowerInvariant();

            absoluteUrlToLower = forceWwwAndLowercase(filterContext, absoluteUrlToLower);
            absoluteUrlToLower = avoidTrailingSlashes(filterContext, absoluteUrlToLower);

            if (!absoluteUrl.Equals(absoluteUrlToLower))
            {
                filterContext.Result = new RedirectResult(absoluteUrlToLower, permanent: true);
            }
        }

        private static string avoidTrailingSlashes(ActionExecutingContext filterContext, string absoluteUrlToLower)
        {
            if (!isRootRequest(filterContext) && absoluteUrlToLower.EndsWith("/"))
                return absoluteUrlToLower.TrimEnd(new[] { '/' });

            return absoluteUrlToLower;
        }

        private static bool isRootRequest(ActionExecutingContext filterContext)
        {
            return filterContext.RequestContext.HttpContext.Request.Url.AbsolutePath == "/";
        }

        private static bool canIgnoreRequest(ActionExecutingContext filterContext)
        {
            return filterContext.IsChildAction || 
                   filterContext.HttpContext.Request.IsAjaxRequest() ||
                   filterContext.RequestContext.HttpContext.Request.Url.AbsoluteUri.Contains("?");
        }

        private static string forceWwwAndLowercase(ActionExecutingContext filterContext, string absoluteUrlToLower)
        {
            if (isLocalRequet(filterContext))
                return absoluteUrlToLower;

            if (absoluteUrlToLower.Contains("www"))
                return absoluteUrlToLower;

            return absoluteUrlToLower.Replace("http://", "http://www.")
                                     .Replace("https://", "https://www.");
        }

        private static bool isLocalRequet(ActionExecutingContext filterContext)
        {
            return filterContext.RequestContext.HttpContext.Request.IsLocal;
        }
    }
}
کلاس فوق، نگارش تکمیل شده ForceWww که پیشتر در این سایت دیده‌اید. توسط آن سه بررسی مختلف بر روی لینک جاری در حال پردازش صورت خواهد گرفت:
- تمام آدرس‌های سایت باید www داشته باشند؛ تا آدرس‌های آن یکنواخت شده و خصوصا مشکلات لاگین و نوشته شدن کوکی‌ها به ازای آدرس‌های مختلف و سر درگمی کاربران کاهش یابد.
- اگر آدرس جاری lowercase نباشد، تبدیل به نمونه lowercase شده و درخواست کننده، به آدرس جدید هدایت می‌شود. این مورد خصوصا جهت موتورهای جستجو برای تصحیح نتایج آن‌ها بسیار مفید است.
- اسلش انتهای لینک‌ها در صورت وجود حذف خواهد شد. این مورد نیز در کاهش تعداد خطاهای «محتوای تکراری» مؤثر است.
- اگر آدرسی، کوئری استرینگ داشته باشد از آن صرفنظر خواهد شد؛ زیرا ممکن است اطلاعات موجود در آن به کوچکی و بزرگی حروف حساس باشند.


ب) کاهش بار سایت توسط تولید خودکار Urlهایی که در بدو امر lowercase هستند

برای پیاده سازی این مطلب می‌توان از پروژه سورس باز «LowercaseRoutesMVC» استفاده کرد. سه فایل cs دارد که می‌توانید به پروژه خود اضافه کنید. پس از آن، هرجایی در پروژه خود routes.MapRoute دارید تبدیل کنید به routes.MapRouteLowercase .
به این ترتیب به صورت خودکار تمام Urlهای تولید شده توسط HTML helpers توکار ASP.NET MVC (و نه Urlهایی که دستی نوشته شده‌اند)، در حین درج در صفحه به صورت lowercase ظاهر خواهند شد (صرفنظر از اینکه نام‌های کنترلرها و یا اکشن متدهای تعریف شده camel case هستند یا خیر). مزیت این مساله کاهش یک مرحله Redirect است که در قسمت الف ذکر شد. در این کتابخانه کمکی نیز از آدرس‌هایی که دارای کوئری استرینگ باشند، صرفنظر می‌شود.
نظرات مطالب
امن سازی برنامه‌های ASP.NET Core توسط IdentityServer 4x - قسمت اول - نیاز به تامین کننده‌ی هویت مرکزی
به علاوه مشکل دیگر توسعه‌ی این نوع برنامه‌های صدور توکن خانگی، اختراع مجدد چرخ است 
با این تفاسیر با فرض اینکه امکان استفاده از providerهای خارجی وجود نداشته باشد,  اگر نیاز باشه دسترسی‌ها و کاربران را خودمان کنترل کنیم بهترین راه حل چیست؟ آیا بهتره یک Identity-Server مجزا جداگانه باید برای مدیریت مرکزی کاربران بین چند برنامه مختلف ایجاد بشه ؟ 
درحال حاضر طبق مطلب بالا من درحال اختراع چرخ بودم (پیاده سازی jwt برای spa کلاینت و موبایل کلاینت)  (چیزی مشابه این مقاله )   و راه جایگزین یک مقدار گنگ هست برام.
و دوم اینکه :
در گذشته، هر تک برنامه‌ای دارای صفحه‌ی لاگین و امکانات مدیریت کاربران آن، تغییر کلمه‌ی عبور، تنظیم مجدد آن و این‌گونه عملیات بود. این‌روزها دیگر چنین کاری مرسوم نیست. این وظیفه‌ی برنامه‌ی شما نیست که بررسی کند کاربر وارد شده‌ی به سیستم کیست و آیا ادعای او صحیح است یا خیر؟ این نوع عملیات وظیفه‌ی یک Identity provider و یا به اختصار IDP است.
در این صورت تهیه سوابق فعالیت‌های کاربر ممکن نیست یا بهتره بگم کمی پیچیده میشه.  مدیریت سوابق فعالیت‌های کاربران به چه صورت باید انجام بشه؟
نظرات اشتراک‌ها
کتابخانه‌ای برای تغییر تصاویر وب‌سایت به صورت هوشمند؛ جهت بهبود کارآیی و سرعت
- برای ارسال مشکلات یک کتابخانه از همان issue tracker آن در GitHub استفاده کنید. همچنین هم عنوان نکنید، «کار نمی‌کنه» چون اصلا مفید نیست و کمکی به رفع مشکل نمی‌کند.
- برای تغییر اندازه تصاویر و امثال اینها در دات نت 5 و 6، می‌توانید از Image sharp استفاده کنید. کتابخانه‌ای هست کاملا نوشته شده با سی‌شارپ، بدون وابستگی‌های native و چندسکویی.
نظرات مطالب
روش‌های مختلف انجام چند کار به صورت همزمان در C# .NET - قسمت اول
حقیقت درک نمیکنم for each که هر بار اجرای حلقه اش زمان بر باشه کجای سناریو ای که گفتید لازم میشه، ولی فرض کنیم لازمه. آموزه‌های بالا فقط باعث میشه کارها سریع‌تر و بهینه‌تر انجام بشن، ولی کمکی به اینکه یه صندلی رو دو نفر بگیرن نمیکنه و باید براش فکری کنید که به این مطلب ارتباطی هم پیدا نمیکنه.