مطالب
Blazor 5x - قسمت 34 - توزیع برنامه‌های Blazor بر روی IIS
زمانیکه صحبت از توزیع برنامه‌های Blazor بر روی IIS است، عموما تنظیمات مرتبط با برنامه‌های Blazor Server و یا Hosted Blazor Apps که همان ترکیب WASM+Web API هستند، مطرح است؛ در غیراینصورت اگر برنامه‌ای صرفا از فایل‌های Blazor WASM تشکیل شده باشد، توزیع آن حتی بر روی صفحات static مربوط به GitHub هم میسر است و وابستگی خاصی به سروری ندارند. بنابراین در اینجا بیشتر هدف تنظیمات IIS مرتبط با قسمت ASP.NET Core این برنامه‌ها است و این مورد را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 22 - توزیع برنامه توسط IIS» بررسی کرده‌ایم و نکته‌ی اضافه‌تری را به همراه ندارد.


مروری بر نحوه‌ی توزیع برنامه‌های Blazor بر روی IIS

1- پیش از هر کاری باید مطابق نگارش ASP.NET Core در حال استفاده (که به عنوان هاست Blazor Server و یا ارائه دهنده‌ی قسمت Web API برنامه‌ی سمت کلاینت WASM مطرح است)، بسته‌ی NET Core hosting bundle. را نصب کرد که عموما تحت عنوان «Hosting Bundle Installer» قابل دریافت است.
نکته‌ی مهم: همانند تمام نگارش‌های دات نت، در اینجا نیز باید Hosting Bundle را پس از نصب IIS، بر روی سیستم نصب کرد. اگر این ترتیب تغییر کند، یکبار دیگر نصاب آن‌را اجرا کرده و گزینه‌ی ترمیم نصب را انتخاب کنید تا یکپارچگی آن با IIS صورت گیرد.

2- نیاز است برنامه‌ی خود را اصطلاحا publish کرد تا به همراه فایل‌های نهایی قابل کپی باشد که در پوشه‌ای توسط IIS هاست خواهند شد. برای اینکار اگر از نگارش کامل ویژوال استودیو استفاده می‌کنید، فقط کافی است بر روی پروژه‌ی مدنظر کلیک راست کرده و از منوی باز شده، گزینه‌ی publish را انتخاب کنید و مراحل آن‌را طی نمائید و یا این مراحل را می‌توان توسط دستور خط فرمان زیر نیز خلاصه کرد که وابستگی خاصی، به IDE ویژه‌ای ندارد و چند سکویی است:
dotnet publish -o "c:\dir1\dir2" -c Release
این دستور، کار Build و توزیع پروژه را در پوشه‌ای که مشخص شده انجام می‌دهد. همچنین برای حالت build، نوع release را انتخاب می‌کند که به همراه بهینه سازی‌های بسیاری به همراه کاهش حجم نهایی فایل‌ها نیز هست.
و یا اگر فقط دستور dotnet publish -c Release را در ریشه‌ی پروژه اجرا کنیم، خروجی نهایی را در پوشه‌ی bin\Release\net5.0\publish می‌توان مشاهده کرد که به همراه یک web.config مخصوص برنامه‌های blazor هم هست و در آن mime typeهای متناظری، به همراه URL rewriting مناسب برنامه‌های تک صفحه‌ای وب از پیش تنظیم شده‌است. بنابراین در اینجا نصب ماژول URL rewrite بر روی IIS نیز الزامی است.

3- در اینجا نیز همانند تنظیمات برنامه‌های ASP.NET Core، باید application pool منتسب به برنامه را ویرایش کرده و NET Clr Version. آن‌را بر روی No Managed Code قرار داد.


روش فعالسازی توزیع مبتنی بر فشرده سازی Brotli در IIS

در حین عملیات publish استاندارد، به صورت پیش‌فرض از تمام فایل‌ها، سه نسخه‌ی اصلی، gz شده (gzip) و یا br شده (فشرده سازی Brotli که فایل‌های کم حجم‌تری را نسبت به gz ارائه می‌دهد) نیز تهیه می‌شوند که بسته به نوع مرورگر و پشتیبانی آن از روش‌های مختلف فشرده سازی، یکی از آن‌ها در اختیار کلاینت قرار خواهد گرفت که به این صورت کاربران، تجربه‌ی دریافت کم حجم‌تر و سریعتری را خواهند داشت.


باید دقت داشت Web.config ای که به همراه دستور dotnet publish ایجاد می‌شود، روش توزیع پیش‌فرض فایل‌های br. تولیدی را ندارد. برای اینکار نیاز است تنظیمات این فایل web.config توصیه شده‌ی توسط تیم Blazor را به web.config خود اضافه کرد تا در نهایت حجم دریافتی از سرور به شدت کاهش یابد.

یک نکته: اگر می‌خواهید فایل web.config سفارشی خودتان را داشته باشید، نمونه‌ای از آن‌را در ریشه‌ی پروژه قرار داده و سپس فایل csproj را به نحو زیر ویرایش کنید تا از آن در حین publish استفاده کند:
<PropertyGroup>
    <PublishIISAssets>true</PublishIISAssets>
</PropertyGroup>


در حین publish برنامه‌های Blazor WASM کار IL trimming نیز انجام می‌شود

برای کاهش حجم نهایی برنامه‌های Blazor WASM، در حین publish در حالت release، کار IL Trimming نیز به صورت خودکار انجام می‌شود تا کدهای IL ای که در برنامه نقش نداشته‌اند و مستقیما در جائی استفاده نشده‌اند، به صورت خودکار حذف شوند و به این ترتیب حجم ارائه‌ی نهایی به شدت کاهش یابد.
فقط باید دقت داشت که در این حالت اگر عملیات پویایی مانند reflection در کدهای شما صورت می‌گیرد، به علت نداشتن ارجاع استاتیکی به منابع مورد استفاده، در زمان اجرا با مشکل مواجه خواهد شد. اگر می‌خواهید اخطارهایی را در این زمینه مشاهده کنید، گزینه‌ی زیر را به فایل csproj اضافه نمائید:
<SuppressTrimAnalysisWarnings>false</SuppressTrimAnalysisWarnings>


حذف مباحث بومی سازی در صورت عدم نیاز


اگر در برنامه‌ی خود از مباحث time-zones استفاده نمی‌کنید، می‌توانید با غیرفعال کردن آن در فایل csproj، حداقل 100 کیلوبایت از حجم برنامه‌ی نهایی را کاهش دهید:
<BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
و یا اگر نمی‌خواهید خروجی‌های تاریخ و یا مقادیر، بر اساس فرهنگ بومی کاربر تطابق پیدا کنند، می‌توانید invariant culture را فعال کنید:
<InvariantGlobalization>true</InvariantGlobalization>
مطالب
خلاصه‌ای کوتاه در مورد WinRT

WinRT چیست؟

مایکروسافت جهت سهولت تولید برنامه‌های جدید Metro-style ، لمسی (touch-centric) و tablets ویندوز 8 ، اقدام به بازنویسی مجدد Windows-API کرده و نام آن‌را WinRT گذاشته است. بنابراین همانند آنچه که در مورد API‌ ویندوز از روز اول پیدایش آن مرسوم بوده، این API جدید، از نوع native (نوشته شده با CPP) می‌باشد و با کمک دات نت فریم ورک تهیه نشده است. این API جدید مبتنی بر فناوری قدیمی COM است، بنابراین مطابق معمول توسط هر نوع برنامه‌ و سیستمی در ویندوز قابل دسترسی است. تفاوتی نمی‌کند که CPP یا دلفی باشد یا دات نت. به صورت خلاصه ویندوز 8 دو طراحی جدید (WinRT) و قدیم (Win32 API) را با هم پشتیبانی می‌کند. اگر آن‌را صحیح‌تر بخواهیم معرفی کنیم، WinRT درحقیقت محصور کننده‌ی (Wrapper) همان Win32 API سابق است (در پشت صحنه همان dll های سابق ویندوز را بارگذاری و استفاده می‌کند) جهت تطابق با نیازهای دهه اخیر و سال‌های پیش رو.


سازگاری دات نت فریم ورک با WinRT چگونه است؟

اینبار WinRT برخلاف Win32 API (که در زمان ارائه آن اصلا دات نتی در کار نبود)، جهت سازگاری با دات نت طراحی شده است. این طراحی جدید ILDasm metadata را در اختیار برنامه نویس‌های دات نت قرار می‌دهد و به این ترتیب IntelliSense و قابلیت‌های Debugging ویژوال استودیو همانند کدهای مدیریت شده‌ی دات نت جهت برنامه نویسی مبتنی بر WinRT در اختیار برنامه نویس‌ها خواهد بود (فرمت ارائه شده، ECMA 335 metadata format می‌باشد). همچنین اشیاء COM متعلق به WinRT به خوبی توسط GC (آشغال جمع کن) دات نت جهت مدیریت بهتر حافظه، تحت نظر می‌باشند.
بنابراین از دیدگاه یک برنامه نویس دات نت، کل WinRT به صورت managed assemblies مشاهده می‌شود، اینترفیس‌های آن همان اینترفیس‌های دات نتی خواهند بود و کلاس‌های آن نیز به همین ترتیب. این مشکلی بود/هست که با Win32 API در دات نت وجود دارد و دسترسی به آن به این سادگی و یکپارچگی میسر نیست (هر چند تا الان کل اینترفیس آن جهت استفاده در دات نت نیز ترجمه شده است). در اینجا شما ارجاعاتی را به فایل‌هایی با پسوند winmd یا windows metadata، به پروژه‌ی دات نتی خود اضافه می‌کنید و سپس CLR قادر خواهد بود تا کلیه اطلاعات لازم جهت کار با WinRT را از آن‌ها استخراج کند (این فایل‌ها در پوشه C:\Program Files (x86)\Windows Kits\8.0\Windows Metadata و C:\Windows\system32\winmetadat ویندوز 8 قابل مشاهده و دسترسی هستند).


تفاوت‌های مهم امکانات نمایشی WinRT با Win32 API کدامند؟

تفاوت مهم WinRT با Win32 API از دیدگاه برنامه نویس‌ها، امکان دسترسی بیشتر به آن از طریق زبان‌های مختلف می‌باشد. WinRT همانند Win32 API توسط CPP ، دات نت و سایر روش‌های مرسوم دیگر قابل دسترسی و توسعه است. اما اینبار WinRT برخلاف Win32 API ، از طریق HTML و جاوا اسکریپت هم قابل توسعه است. در این حالت کدهای شما توسط Chakra JavaScript engine که از اینترنت اکسپلورر 9 به بعد ارائه شده، اجرا خواهد شد. بنابراین «برفراز» WinRT دو لایه نمایشی (presentation layer) قابل طراحی و دسترسی است. XAML و زبان‌های متداول برنامه نویسی موجود مانند سی شارپ و وی بی دات نت و غیره. همچنین HTML/CSS هم مجال ابراز وجود یافته است. البته XAML تنها لایه نمایشی کلیه زبان‌های قدیمی موجود مانند سی شارپ، وی بی دات نت، CPP و غیره خواهد بود. به همین جهت Expression Blend جدید نیز از HTML 5 پشتیبانی می‌کند.
همچنین در WinRT ، قسمت‌های GDI و Message loop متداول Win32 API حذف شده است و از DirectX استفاده می‌کند. برای نمونه کدهای XAML شما توسط DirectX رندر می‌شود. البته این مطلب جدیدی نیست و از زمان ارائه WPF شاهد این مساله بوده‌ایم.


وضعیت توسعه پذیری WinRT  چگونه است؟

علاوه بر این‌ها، برخلاف Win32 API ، اینبار WinRT قابل توسعه است و Extensions SDK برای آن ارائه شده است.


آیا WinRT شاهد تغییرات امنیتی خاصی هم بوده است؟

نکته‌ی مهمی که در طراحی WinRT به آن توجه شده است، امنیت می‌باشد. برنامه‌های WinRT شبیه به برنامه‌های سیلورلایت در یک Sandbox اجرا می‌شوند. به این معنا که جهت ذخیره سازی اطلاعات خود از یک isolated storage استفاده می‌کنند. برای کار با file system نیاز به تائید کاربر دارند و خلاصه دیگر به سادگی نمی‌توان از مرزهای این نوع برنامه‌ها رد شد و سیستم عاملی را root کرد. برای نمونه برنامه نویس‌های دات نت دسترسی به فضای نام System.IO.FileStream را دیگر نخواهند داشت و تنها قسمتی از دات نت «برفراز» WinRT و مدل امنیتی جدید آن معنا پیدا می‌کند. همچنین برفراز این API جدید، تولید مثلا Device drivers هم دیگر معنا پیدا نمی‌کند. این محدودیت‌های امنیتی برای برنامه‌ نویس‌های native هم وجود دارد و تفاوتی نمی‌کند. کلا برنامه‌های جدید Metro-style در یک قرنطینه‌ی کامل امنیتی اجرا می‌شوند. برای مثال اگر برنامه‌ای نیاز به دسترسی به یک WebCam را داشته باشد، همانند برنامه‌های سیلورلایت ابتدا باید کاربر تائید کرده و سپس برنامه مجوز امنیتی کار با مثلا یک WebCam را خواهد یافت. همچنین تمام برنامه‌های جدید Metro-style باید جهت ارائه در فروشگاه جدید ویندوز 8، دارای امضای دیجیتال معتبر نیز باشند.


آیا جهت توسعه‌ی برنامه‌های چندریسمانی و غیرهمزمان تمهیدات خاصی در WinRT پیش‌بینی شده است؟

در طراحی جدید WinRT به اعمال asynchronous به شدت توجه شده است. هر عملی که بیش از 50 میلی ثانیه طول بکشد به صورت خودکار تبدیل به یک عمل asynchronous خواهد شد تا برنامه‌ها مرتبا در حین اجرای اعمال زمانبر هنگ نکرده و ترد اصلی برنامه را بلاک نکنند. علاوه بر این‌ها WinRT از طریق IAsyncOperation interface خود، امکان استفاده از واژه‌های جدید کلیدی async/await سی شارپ 5 را نیز مهیا می‌سازد.


آیا WinRT آمده است تا جایگزینی برای دات نت و سیلورلایت و امثال آن باشد؟

خیر. WinRT نگارش دوم Win32 API است با هدف توسعه پذیری، استفاده از دایرکت ایکس و فناوری‌های جدید که عموما از شتاب دهنده‌های سخت افزاری هم بهره‌مند هستند بجای GDI سابق، استفاده ساده‌تر در زبان‌های دیگر به کمک فایل‌های استاندارد Windows Meta data آن می‌باشد. همچنین این API جدید دسترسی به امکانات ویندوز را هم توسط HTML و جاوا اسکریپت، علاوه بر امکانات مهیای سابق میسر ساخته است. هم اکنون نگارش 4 و نیم دات نت در ویندوز 8 ارائه شده است و توسط هر دو سیستم سابق و جدید قابل استفاده می‌باشد. البته باید در نظر داشت که جهت استفاده از WinRT به دلایل محدودیت‌های امنیتی اعمال شده به آن و همچنین استفاده از XAML به تنها عنوان لایه نمایشی سیستم‌های متداول غیر HTML ایی، دات نت فریم ورک به امکانات و کلاس‌های کمتری نسبت به حالت متداول کار با آن، دسترسی دارد (جهت درک بهتر این محدودیت‌ها می‌توان به طراحی سیلورلایت مراجعه کرد). این را هم باید اضافه کرد که ویندوز 8 توانایی اجرای هر دو نوع برنامه‌های سبک جدید مترو و متداول دسکتاپ قدیمی را دارا است.


جهت آشنایی بیشتر با WinRT می‌توان به مجموعه‌ای از ویدیوهای مرتبط آن مراجعه کرد:
http://channel9.msdn.com/Events/BUILD/BUILD2011?t=windows%2Bruntime


نظرات مطالب
اعتبارسنجی مبتنی بر JWT در ASP.NET Core 2.0 بدون استفاده از سیستم Identity
چطور میتوانیم با استفاده از ASP.NET Core Identity در موبایل لاگین کنیم. یعنی بخوایم برنامه نویسی اندروید انجام بدیم و با استفاده از Api لاگین کنیم و یک سری کارهایی انجام بدیم
مطالب
ایجاد سرویس Account Manager با تکنولوژی های Identity 2.1 و Web API 2.2
ASP.NET Identity 2.1 جدیدترین فریم ورک عضویت و مدیریت کاربر است که چندی پیش توسط شرکت مایکروسافت منتشر شد. این سیستم عضویت می‌تواند به تمامی فریم‌ورک‌های دات نتی مانند Web API، MVC و ... متصل گردد.
در این دوره چند قسمتی به همراه یک پروژه‌ی نمونه، نحوه‌ی ارتباط Identity و Web API را نمایش خواهیم داد. در قسمت front-end این پروژه‌ی SPA، ما از AngularJs استفاده خواهیم نمود. قسمت front-end که توسط AngularJs توسعه داده می‌شود از bearet token based authentication استفاده می‌کند. این متد از JSON Web Token برای فرایند Authorization بهره می‌گیرد که به اختصار آن را JWT نیز می‌نامند. این روش سیستم اعتبار سنجی role based بوده و  تمامی آنچه را که در یک سیستم membership داریم، پوشش می‌دهد. توجه داشته باشید که برای فهم بهتر تمامی مراحل، ما پروژه را بدون هیچ قالب از پیش تعریف شده‌ای در VS2013 آغاز می‌کنیم.
به دلیل اینکه پروژه در طی چند قسمت انجام می‌پذیرد آن را به بخش‌های زیر تقسیم می‌کنیم.
  1.  تنظیمات اولیه ASP.NET Identity 2.1 با Web API
  2.  ایجاد Account Confirmation به وسیله Identity به همراه تنظیمات policy برای user name و password
  3.  توسعه OAuth Json Web Token Authentication به کمک Web API و Identity
  4.  ایجاد یک سیستم Role Based و تایید صلاحیت‌های مربوط به آن
  5.  توسعه Web API Claims Authorization در Identity 2.1
  6.  توسعه بخش front-end با AngularJs

تنظیمات اولیه ASP.NET Identity 2.1 با Web API 

1. تنظیمات ASP.Net Identity 2.1

1-1. ساخت یک پروژه Web API

در ابتدا ما یک empty solution را با نام "AspNetIdentity" همانند شکل مقابل می‌سازیم.

یک ASP.NET Web Application با نام "AspNetIdentity.WebApi" را به این solution اضافه می‌نماییم. در بخش select template ما empty template را انتخاب می‌کنیم. همچنین در قسمت add folders and core references for: نیز هیچیک از گزینه‌ها را انتخاب نمی‌کنیم.

1-2. نصب package‌های مورد نیاز از Nuget

در زیر package‌های مورد نیاز برای ASP.NET Web API و Owin را مشاهده میکنید. همچنین package‌های مربوط به ASP.Net Identity 2.1 نیز در زیر قرار داده شده‌اند.

Install-Package Microsoft.AspNet.Identity.Owin -Version 2.1.0
Install-Package Microsoft.AspNet.Identity.EntityFramework -Version 2.1.0
Install-Package Microsoft.Owin.Host.SystemWeb -Version 3.0.0
Install-Package Microsoft.AspNet.WebApi.Owin -Version 5.2.2
Install-Package Microsoft.Owin.Security.OAuth -Version 3.0.0
Install-Package Microsoft.Owin.Cors -Version 3.0.0

1-3. اضافه کردن user class و database context

حال که تمامی پکیج‌های مورد نیاز را به پروژه خود اضافه نمودیم، قصد داریم تا اولین کلاس EF را با نام "ApplicationUser" به پروژه اضافه کنیم. این کلاس، کاربری را که قصد ثبت نام در membership system، دارد را نمایش می‌دهد. برای این کار ما یک کلاس جدید را به نام "ApplicationUser" می‌سازیم و کلاس "Microsoft.AspNet.Identity.EntityFramework.IdentityUser" را در آن به ارث می‌بریم.

برای این کار ما یک پوشه‌ی جدید را در برنامه با نام "Infrastructure" می‌سازیم و درون آن کلاس ذکر شده را اضافه می‌کنیم:

 public class ApplicationUser : IdentityUser
    {
        [Required]
        [MaxLength(100)]
        public string FirstName { get; set; }
 
        [Required]
        [MaxLength(100)]
        public string LastName { get; set; }
 
        [Required]
        public byte Level { get; set; }
 
        [Required]
        public DateTime JoinDate { get; set; }
 
    }

پس از افزودن کلاس User، نوبت به اضافه نمودن Db Context است. این کلاس وظیفه‌ی ارتباط با پایگاه داده را بر عهده دارد. ما یک کلاس جدید را با نام ApplicationDbContext، به پوشه‌ی Infrastructure اضافه می‌نماییم. کد مربوط به این کلاس به صورت زیر است:

 public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext()
            : base("DefaultConnection", throwIfV1Schema: false)
        {
            Configuration.ProxyCreationEnabled = false;
            Configuration.LazyLoadingEnabled = false;
        }
 
        public static ApplicationDbContext Create()
        {
            return new ApplicationDbContext();
        }
 
    }

همانطور که ملاحظه می‌کنید این کلاس از IdentityDbContext ارث بری نموده است. این کلاس یک نسخه‌ی جدیدتر از DbContext است که تمامی نگاشت‌های Entity Framework Code First را انجام می‌دهد. در ادامه ما یک Connection String را با نام DefaultConnection در فایل web.config اضافه می‌نماییم.

همچنین متد static ایی را با نام Create که در تکه کد فوق از سوی Owin Startup class فراخوانی می‌گردد که در ادامه به شرح آن نیز خواهیم پرداخت.

ConnectionString قرار داده شده در فایل web.config در قسمت زیر قرار داده شده است:

<connectionStrings>
    <add name="DefaultConnection" connectionString="Data Source=.\sqlexpress;Initial Catalog=AspNetIdentity;Integrated Security=SSPI;" providerName="System.Data.SqlClient" />
  </connectionStrings>

قدم 4: ساخت پایگاه داده و فعال سازی DB Migration

حال ما باید EF CodeFirst Migration را برای آپدیت کردن دیتابیس، بجای دوباره ساختن آن به ازای هر تغییری، فعال نماییم. برای این کار بایستی در قسمت NuGet Package Manager Console عبارت زیر را وارد نماییم:

enable-migrations
add-migration InitialCreate

اگر تا به اینجای کار تمامی مراحل به درستی صورت گرفته باشند، پوشه‌ی Migration با موفقیت ساخته می‌شود. همچنین پایگاه داده متشکل از جداول مورد نیاز برای سیستم Identity نیز ایجاد می‌شود. برای اطلاعات بیشتر می‌توانید مقالات مرتبط با Code First را مطالعه نمایید. ساختار جداول ما باید به صورت زیر باشد:

در بخش بعدی، کلاس‌های مربوط به UserManager را ایجاد خواهیم کرد و پس از آن فایل Startup Owin را برای مدیریت کاربران، تشریح می‌کنیم.

مطالب
Minimal API's در دات نت 6 - قسمت اول - معرفی
یکی از مهم‌ترین تغییرات دات نت 6، ارائه‌ی Minimal API's به همراه آن است که نسبت به MVC و سایر مشتقات ASP.NET Core، کمتر به همراه پیش‌فرض‌های نظری خاص و بسیار مقید و متعصبانه (opinionated) است؛ که این مورد خود مزیتی است جهت انجام امور متداول، به نحوی دیگر و دلخواه و با آزادی عمل بیشتری در طراحی endpoints مورد نیاز و کل برنامه. خصوصا این سبک جدید، با معماری برش‌های عمودی (vertical slices) ارائه شده‌ی توسط نویسنده‌ی AutoMapper، هماهنگی خاصی دارد و اینطور به نظر می‌رسد که جهت ساده سازی طراحی برنامه‌های ASP.NET Core با معماری CQRS ارائه شده‌است. با وجود Minimal API's می‌توان از دو لایه‌ی متداول برنامه‌ها رها شد: لایه‌ی سرویس‌ها و لایه‌ی مخازن یا Repositories. در معماری برش‌های عمودی، برنامه به ویژگی‌های خاصی (Features) تقسیم شده و هر ویژگی، متکی به خود طراحی می‌شود. زمانیکه از هندلرها برای هر Command و Query معماری CQRS استفاده می‌کنیم، این‌ها مختص به یک ویژگی متکی به خود طراحی می‌شوند و به همراه تمام اطلاعات و اعمال مرتبط هستند و دیگر در این حالت، وجود لایه‌های سرویس و مخزن، بی‌معنا و غیرضروری می‌شوند.

در ادامه قصد داریم تمام این موارد را مانند Minimal API's و معماری برش‌های عمودی به همراه CQRS، در طی یک سری و یک پروژه‌ی عملی ساخت یک Blog به نام MinimalBlog، بررسی کنیم. البته هدف ما در اینجا صرفا ساخت backend ساختار یافته‌ی این برنامه‌است؛ منهای UI آن. هدف اصلی ما از این سری، ارائه‌ی یک معماری، جهت کار با Minimal API's است.


دریافت کدهای کامل این سری

جهت مرور سریعتر و ساده‌تر این سری، کدهای کامل آن‌را از اینجا می‌توانید دریافت کنید: MinimalBlog.zip


پروژه‌هایی که برنامه‌ی MinimalBlog را تشکیل می‌دهند

برنامه‌ی MinimalBlog، تنها از سه پروژه‌ی زیر تشکیل می‌شود:
MinimalBlog.Api: این پروژه از نوع minimal API's است که توسط دستور جدید «dotnet new webapi --use-minimal-apis» آغاز خواهد شد و به صورت پیش‌فرض به همراه پشتیبانی از OpenAPI نیز هست. البته اگر از VS2022 استفاده می‌کنید، در حین آغاز یک پروژه‌ی Web API جدید، تیک مربوط به use controllers را در UI بردارید تا از Minimal API's استفاده شود.
MinimalBlog.Dal: که Dal در اینجا مخفف data access layer است و یک class library می‌باشد و با دستور dotnet new classlib آغاز می‌شود.
MinimalBlog.Domain: نیز یک class library است و با دستور dotnet new classlib آغاز می‌شود.

همانطور که مشاهده می‌کنید، این طراحی جدید، بدون وجود لایه‌ی متداول سرویس‌ها و یا مخازن است.


بررسی ساختار ابتدایی پروژه‌ی MinimalBlog.Api

در اینجا تنها تک فایل Program.cs، به همراه تنظیمات برنامه قابل مشاهده‌است و فایل Starup.cs از آن حذف شده‌است (اطلاعات بیشتر). این فایل نیز بر مبنای مفهوم top level programs طراحی شده‌است و به همراه تعریف class و یا فضای نامی نیست:
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
همانطور که ملاحظه می‌کنید، تمام اتفاقات در همین تک فایل رخ می‌دهند. برای مثال سرویس‌های مورد نیاز برنامه به مجموعه‌ی builder.Services اضافه می‌شوند؛ شبیه به کاری که پیشتر در فایل Startup.cs و متد ConfigureServices آن انجام می‌دادیم.

پس از آن به تعاریف زیر می‌رسیم؛ تعاریف میان افزارهایی که پیشتر در متد Configure کلاس Startup انجام می‌شدند، الان همگی در تک فایل Program.cs قرار دارند:
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpsRedirection();
البته هنوز هم می‌توان در صورت نیاز به همان ساختار کلاس Startup پیشین نیز رسید.


در انتهای این فایل نیز تعاریف پیش‌فرض زیر قرار دارند:
var summaries = new[]
{
    "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};

app.MapGet("/weatherforecast", () =>
{
    var forecast =  Enumerable.Range(1, 5).Select(index =>
        new WeatherForecast
        (
            DateTime.Now.AddDays(index),
            Random.Shared.Next(-20, 55),
            summaries[Random.Shared.Next(summaries.Length)]
        ))
        .ToArray();
    return forecast;
})
.WithName("GetWeatherForecast");

app.Run();

record WeatherForecast(DateTime Date, int TemperatureC, string? Summary)
{
    public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
در اینجا متد متد MapGet یک endpoint را تعریف کرده و سپس اکشنی را به آن انتساب می‌دهد. یعنی اگر آدرس weatherforecast/ درخواست شود، lambda expression تعریف شده، اجرا می‌شود. هدف از ارائه‌ی Minimal API نیز همین است تا بتوان با حداقل کدنویسی، سریعا به نتیجه‌ی مدنظر خود رسید.
در همین حال اگر برنامه‌ی Api را اجرا کنیم، به تصویر زیر خواهیم رسید:


در ادامه کدهای موجود در این فایل را Refactor کرده و به کلاس‌های دیگری منتقل می‌کنیم؛ چون اگر قرار باشد در طول زمان تمام endpoints مدنظر را در همینجا تعریف کنیم، کنترل برنامه از دست خارج خواهد شد.


غنی سازی Solution و کامپایلر #C با استفاده از فایل‌های editorconfig. و Directory.Build.props

در مورد این دو فایل در مطلب «غنی سازی کامپایلر C# 9.0 با افزونه‌ها » بیشتر بحث شده‌است. هدف از آن‌ها، اعمال یکسری تنظیمات سراسری، به تمام پروژه‌های یک solution به صورت یک‌دست است؛ مانند تنظیمات کامپایلر جهت نمایش اخطارها به صورت خطاها، تعریف usingهای سراسری سی‌شارپ 10 و یا اعمال Roslyn analyzers به تمام پروژه‌ها. این دو فایل را به همراه پروژه‌ی پیوست می‌توانید دریافت کنید و ... باید جزء استاندارد تمام پروژه‌های جدید باشند. چون وجود آن‌ها سبب خواهد شد که به شدت کیفیت کدهای نهایی افزایش یابند و مبتنی بر یکسری best practices شوند.
مطالب
چه زمانی بهتر است از بانک‌های اطلاعاتی NoSQL استفاده کرد و چه زمانی خیر؟
در سناریوهای خاصی، بانک‌های اطلاعاتی NoSQL خوش می‌درخشند و در بسیاری از موارد دیگر، بانک‌های اطلاعاتی رابطه‌ای بهترین گزینه انتخابی می‌باشند و نه بانک‌های اطلاعاتی NoSQL. در ادامه به بررسی این موارد خواهیم پرداخت.


در چه برنامه‌هایی استفاده از بانک‌های اطلاعاتی NoSQL مناسب‌تر است؟
1) برنامه‌های مدیریت محتوا
2) کاتالوگ‌های محصولات (هر برنامه‌ای با تعدادی شیء و خصوصا متادیتای متغیر)
3) شبکه‌های اجتماعی
4) Big Data
5) سایر


1) برنامه‌های مدیریت محتوا
بانک‌های اطلاعاتی NoSQL سندگرا، جهت تهیه برنامه‌های مدیریت محتوا، بسیار مناسب هستند. در این نوع برنامه‌ها، یک سری محتوا که دارای متادیتایی هستند، ذخیره خواهند شد. این متادیتاها مانند نوع، گروه و هر نوع خاصیت دیگری، می‌تواند باشند. برای ذخیره سازی این نوع اطلاعات، جفت‌های key-value بسیار خوب عمل می‌کنند. همچنین در بانک‌های اطلاعاتی سندگرای NoSQL، با استفاده از مفهوم برچسب‌ها، امکان الصاق فایل‌های متناظری به اسناد پیش بینی شده‌است. همانطور که در قسمت قبل نیز ذکر شد، در Document stores، نگارش‌های قدیمی اسناد نیز حفظ می‌شوند. به این ترتیب، این خاصیت و توانمندی توکار، امکان دسترسی به نگارش‌های مختلف یک محتوای خاص را به سادگی میسر می‌سازد. به علاوه اکثر Document stores امکان دسترسی به این مستندات را به کمک URLها و REST API، به صورت خودکار فراهم می‌سازند.
برای نمونه به CouchDB، عنوان Web database نیز داده شده است؛ از این جهت که یک برنامه وب را می‌توان داخل بانک اطلاعاتی آن قرار داد. در اینجا منظور از برنامه وب، یک وب سایت قابل دسترسی از طریق URLها است و نه برنامه‌های سازمانی وب. برای نمونه ساختاری شبیه به برنامه معروف EverNote را می‌توان داخل این نوع بانک‌های اطلاعاتی به سادگی ایجاد کرد (خود بانک اطلاعاتی تشکیل شده است از یک وب سرور که REST API را پشتیبانی کرده و امکان دسترسی به اسناد را بدون نیاز به کدنویسی اضافه‌تری، از طریق URLها و HTTP Verbs استاندارد مهیا می‌کند).


2) کاتالوگ‌های محصولات
محصولات در یک کاتالوگ، ویژگی‌های مشابه یکسان فراوانی دارند؛ اما تعدادی از این محصولات، دارای ویژگی‌هایی خاص و منحصربفردی نیز می‌باشند.
مثلا یک شیء محصول را درنظر بگیرید که دارای خواص مشترک و یکسان شماره، نام، توضیحات و قیمت است. اما بعضی از محصولات، بسته به رده‌ی خاصی که دارند، دارای ویژگی‌های خاصی مانند قدرت تفکیک، رنگ، سرعت و غیره نیز هستند که از هر گروه، به گروه دیگری متغیر است.
برای مدیریت یک چنین نیازی، هر دو گروه key-value stores و wide column stores بانک‌های اطلاعاتی NoSQL مناسب هستند؛ از این جهت که در یک key-value store نیازی به تعریف هیچ نوع ساختار خاصی، در ابتدای کار نیست و این ساختار می‌تواند از هر رکورد، به رکورد دیگری متفاوت باشد.
یا برای نمونه، یک برنامه فرم ساز را درنظر بگیرید که هر فرم آن، هر چند دارای یک سری خواص ثابت مانند نام، گروه و امثال آن است، اما هر کدام دارای فیلدهای تشکیل دهنده متفاوتی نیز می‌باشد. به این ترتیب با استفاده از key-value stores، دیگری نیازی به نگران بودن در مورد نحوه مدیریت اسکیمای متغیر مورد نیاز، نخواهد بود.


3) شبکه‌های اجتماعی
همانطور که در قسمت قبل نیز بحث شد، نوع خاص Graph databases برای کاربردهای برنامه‌های شبکه‌های اجتماعی و ردیابی تغییرات آن‌ها بسیار مفید و کارآ هستند. برای مثال در یک شبکه افراد دارای تعدادی دنبال کننده هستند؛ عضو گروه‌های مختلف می‌باشند، در قسمت‌های مختلفی نظر و مطلب ارسال می‌کنند. در اینجا، اشیاء نسبت به یکدیگر روابط مختلفی دارند. با استفاده از Graph databases، تشکیل روابط self-joins و تو در تو و بسیاری از روش‌های خاص، مانند روابط many-to-many که در بانک‌های اطلاعاتی رابطه‌ای با تمهیدات ویژه‌ای قابل تشکیل هستند، با سهولت بهتری مدیریت خواهند شد.


4) Big Data
الگوریتم MapReduce، برای کار با حجم داده‌های عظیم، طراحی شده است و در این بین، بانک‌های اطلاعاتی Wide column store (که در قسمت قبل بررسی شدند) و یا حتی Key-value store (مانند Amazon DynamoDB) بیشتر کاربرد دارند. در سناریوهای داده‌های عظیم، واژه‌های Hadoop و Hbase دنیای NoSQL را زیاد خواهید شنید. Hadoop نسخه سورس باز MapReduce گوگل است و Hbase نیز نسخه سورس باز BigTable گوگل می‌باشد. مفاهیم پایه‌ای Sharding و فایل سیستم‌های Append-only (با سرعت بالای نوشتن) نیز به مدیریت BigData کمک می‌کنند.
در اینجا بحث مهم، خواندن اطلاعات و آنالیز آن‌ها است و نه تهیه برنامه‌های معروف CRUD. بسیاری از اعمال آماری و ریاضی مورد نیاز بر روی داده‌های عظیم، نیازی به اسکیمای از پیش مشخص شده بانک‌های اطلاعاتی رابطه‌ای را ندارند و یا در اینجا قابلیت‌های نوشتن کوئری‌های پیچیده نیز آنچنان مهم نیستند.


5) سایر کاربردها
- هر سیستمی که اطلاعات Log مانند را تولید می‌کند. منظور از Log، اطلاعاتی است که در حین رخداد خاصی تولید می‌شوند. عموما مرسوم است که این نوع اطلاعات را در فایل‌ها، بجای بانک اطلاعاتی ذخیره کرد. بنابراین مدیریت این نوع فایل‌ها توسط بانک‌های اطلاعاتی NoSQL، قابلیت انجام امور آماری را بر روی آن‌ها ساده‌تر خواهد ساخت.
- مدیریت اطلاعات برنامه‌هایی مانند سیستم‌های EMail.



و در چه برنامه‌هایی استفاده از بانک‌های اطلاعاتی رابطه‌ای مناسب‌تر است؟

اگر تا اینجا به مزایای استفاده از بانک‌های اطلاعاتی NoSQL اشاره شد، بدین معنا نیست که بانک‌های اطلاعاتی رابطه‌ای، منسوخ شده‌اند یا دیگر قدر و قیمتی ندارند. واقعیت این است که هنوز بازه وسیعی از کاربردها را می‌توان به کمک بانک‌های اطلاعاتی رابطه‌ای بهتر از بانک‌های اطلاعاتی NoSQL مدیریت کرد. این کاربردها و مزیت‌ها در 5 گروه عمده خلاصه می‌شوند:
1) نیاز به تراکنش‌ها
2) اسکیمای پیش فرض
3) برنامه‌های LOB یا Line of business applications
4) زبان‌های کوئری نویسی پیشرفته
5) نیاز به امکانات گزارشگیری پیشرفته


1) نیاز به تراکنش‌ها
در سیستم‌های تجاری عمومی، نیاز به پیاده سازی مفهوم ACID که در قسمت‌های قبل به آن پرداخته شد، مانند Atomic transactions وجود دارد. Atomic transaction به زبان ساده به این معنا است که سیستم قادر است چندین دستور را در قالب یک گروه و در طی یک مرحله، به بانک اطلاعاتی اعمال کند و اگر یکی از این دستورات گروه در حال اعمال، با شکست مواجه شد، باید کل تراکنش برگشت خورده و امنیت کار تضمین گردد. در غیراینصورت با یک سیستم غیر هماهنگ مواجه خواهیم شد.
و همانطور که پیشتر نیز عنوان شد، سیستم‌های NoSQL، مبنای کار را بر اساس «عاقبت یک دست شدن» اطلاعات قرار داده‌اند؛ تا دسترسی پذیری به آن‌ها افزایش یافته و سرعت عملیات به این نحو بهبود یابد. در این نوع سیستم‌ها تضمینی در مورد ACID وجود ندارد.


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


3) برنامه‌های LOB یا Line of business applications
در برنامه‌های تجاری متداول، طراحی طرحبندی فرم‌های برنامه یا انقیاد داده‌ها، بر اساس یک اسکیما و ساختار مشخص صورت می‌گیرد. بدون داشتن یک اسکیمای مشخص، امکان تعاریف انقیاد داده‌ها به صورت strongly typed وجود نخواهد داشت. همچنین کل مفهوم Object relational mapping و ORMهای مختلف نیز بر اساس وجود یک اسکیمای مشخص و از پیش تعیین شده کار می‌کند. بنابراین بانک‌های اطلاعاتی رابطه‌ای، انتخاب بسیار مناسبی برای تهیه برنامه‌های تجاری روزمره هستند.


4) زبان‌های کوئری نویسی پیشرفته
همانطور که عنوان شد برای تهیه کوئری بر روی اغلب بانک‌های اطلاعاتی NoSQL، باید توسط یک برنامه ثانویه، کار پیاده سازی الگوریتم Map Reduce را انجام داد. هر چند تعدادی از این نوع بانک‌های اطلاعاتی به صورت توکار دارای موتور MapReduce هستند، اما بسیاری از آن‌ها خیر. به همین جهت برای تهیه کوئری‌های متداول، کار پیاده سازی این برنامه‌های ثانویه مشکل خواهد بود. به این ترتیب نوشتن Ad Hoc queries و گزارشگیری بسیار مشکل می‌شوند.
علاوه بر امکانات خوب کوئری گرفتن در بانک‌های اطلاعاتی رابطه‌ای، این کوئری‌ها در زمان اجرا نیز بر اساس اسکیمای موجود، بسیار بهینه و با سرعت بالا اجرا می‌شوند. قابلیتی که رسیدن به آن در بانک‌های اطلاعاتی با اسکیمای متغیر، کار ساده‌ای نیست و باید آن‌را با کدنویسی شخصی بهینه کرد. البته اگر تعداد این نوع برنامه‌های ثانویه که به آن‌ها imperative query در مقابل declarative query بانک‌های رابطه‌ای می‌گویند، کم باشد، شاید یکبار نوشتن و بارها استفاده کردن از آن‌ها اهمیتی نداشته باشد؛ در غیراینصورت تبدیل به یک عذاب خواهد شد.


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

بنابراین به صورت خلاصه، بانک‌های اطلاعاتی رابطه‌ای، جهت مدیریت کارهای روزمره تجاری اغلب شرکت‌ها، بسیار ضروری و جزو مسایل پایه‌ای به‌شمار می‌روند و به این زودی‌ها هم قرار نیست با نمونه‌ی دیگری جایگزین شوند.
 
مطالب
هاست سرویس های Asp.Net Web Api با استفاده از OWIN و TopShelf
زمانیکه از Template‌های پیش فرض تدارک دیده شده در VS.Net برای اپلیکیشن‌های وب خود استفاده می‌کنید، وب اپلیکیشن و سرور با هم یکپارچه هستند و تحت IIS  اجرا می‌شوند. به وسیله Owin می‌توان این دو مورد را بدون وابستگی به IIS به صورت مجزا اجرا کرد. در این پست قصد داریم سرویس‌های Web Api را در قالب یک Windows Service با استفاده از کتابخانه‌ی TopShelf هاست نماییم.
پیش نیاز ها:
»Owin چیست
»تبدیل برنامه‌های کنسول ویندوز به سرویس ویندوز ان تی

  برای شروع یک برنامه Console Application ایجاد کرده و اقدام به نصب پکیج‌های زیر نمایید:
Install-Package Microsoft.AspNet.WebApi.OwinSelfHost
Install-Package TopShelf

حال یک کلاس Startup برای پیاده سازی Configuration‌های مورد نیاز ایجاد می‌کنیم
در این قسمت می‌توانید تنظیمات زیر را پیاده سازی نمایید:
»سیستم Routing؛
»تنظیم  Dependency Resolver برای تزریق وابستگی کنترلر‌های Web Api؛
»تنظیمات hub‌های SignalR(در حال حاضر SignalR به صورت پیش فرض نیاز به Owin برای اجرا دارد)؛
»رجیستر کردن Owin Middleware‌های نوشته شده؛
»تغییر در Asp.Net PipeLine؛
»و...

public class Startup 
    {       
        public void Configuration(IAppBuilder appBuilder) 
        {          
            HttpConfiguration config = new HttpConfiguration(); 
            config.Routes.MapHttpRoute( 
                name: "DefaultApi", 
                routeTemplate: "api/{controller}/{id}", 
                defaults: new { id = RouteParameter.Optional } 
            ); 

            appBuilder.UseWebApi(config); 
        } 
    }
* به صورت پیش فرض نام این کلاس باید Startup و نام متد آن نیز باید Configuration باشد.

در این مرحله یک کنترلر Api به صورت زیر به پروژه اضافه نمایید:
public class ValuesController : ApiController 
    {        
        public IEnumerable<string> Get() 
        { 
            return new string[] { "value1", "value2" }; 
        } 
      
        public string Get(int id) 
        { 
            return "value"; 
        } 

        public void Post([FromBody]string value) 
        { 
        } 

        public void Put(int id, [FromBody]string value) 
        { 
        }        
    }
کلاسی به نام ServiceHost ایجاد نمایید و کد‌های زیر را در آن کپی کنید:
public class ServiceHost
    {     
        private IDisposable webApp;             

        public static string BaseAddress 
        {
            get
            {
                return "http://localhost:8000/";
            }
        }

        public void Start()
        {           
            webApp = WebApp.Start<Startup>(BaseAddress);          
        }

        public void Stop()
        {           
            webApp.Dispose();          
        }
    }
واضح است که متد Start در کلاس بالا با استفاده از متد Start کلاس WebApp، سرویس‌های Web Api را در آدرس مورد نظر هاست خواهد کرد. با فراخوانی متد Stop این سرویس‌ها نیز dispose خواهند شد.
در مرحله آخر باید شروع و توقف سرویس‌ها را تحت کنترل کلاس HostFactory کتابخانه TopShelf در آوریم. برای این کار کافیست کلاسی به نام ServiceHostFactory ایجاد کرده و کد‌های زیر را در آن کپی نمایید:
public class ServiceHostFactory
    {
        public static void Run()
        {
            HostFactory.Run( config =>
            {
                config.SetServiceName( "ApiServices" );
                config.SetDisplayName( "Api Services ]" );
                config.SetDescription( "No Description" );

                config.RunAsLocalService();

                config.Service<ServiceHost>( cfg =>
                {
                    cfg.ConstructUsing( builder => new ServiceHost() );

                    cfg.WhenStarted( service => service.Start() );
                    cfg.WhenStopped( service => service.Stop());
                } );
            } );
        }
    }
توضیح کد‌های بالا:
ابتدا با فراخوانی متد Run سرویس مورد نظر اجرا خواهد شد. تنظیمات نام سرویس و نام مورد نظر جهت نمایش  و همچنین توضیحات در این قسمت انجام می‌گیرد.
با استفاده از متد ConstructUsing عملیات وهله سازی از سرویس انجام خواهد گرفت. در پایان نیز متد Start  و Stop کلاس ServiceHost، به عنوان عملیات شروع و پایان سرویس ویندوز مورد نظر تعیین شد.

حال اگر در فایل Program پروژه، دستور زیر را فراخوانی کرده و برنامه را ایجاد کنید خروجی زیر قابل مشاهده است.
ServiceHostFactory.Run();

در حالیکه سرویس مورد نظر در حال اجراست، Browser را گشوده و آدرس http://localhost:8000/api/values/get را در AddressBar وارد کنید. خروجی زیر را مشاهده خواهید کرد: