یکی از مهمترین تغییرات دات نت 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 و یا فضای نامی نیست:
همانطور که ملاحظه میکنید، تمام اتفاقات در همین تک فایل رخ میدهند. برای مثال سرویسهای مورد نیاز برنامه به مجموعهی builder.Services اضافه میشوند؛ شبیه به کاری که پیشتر در فایل Startup.cs و متد ConfigureServices آن انجام میدادیم.
پس از آن به تعاریف زیر میرسیم؛ تعاریف میان افزارهایی که پیشتر در متد Configure کلاس Startup انجام میشدند، الان همگی در تک فایل Program.cs قرار دارند:
البته هنوز هم میتوان در صورت نیاز به همان ساختار کلاس Startup پیشین نیز رسید.
در انتهای این فایل نیز تعاریف پیشفرض زیر قرار دارند:
در اینجا متد متد 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 شوند.
در ادامه قصد داریم تمام این موارد را مانند 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();
پس از آن به تعاریف زیر میرسیم؛ تعاریف میان افزارهایی که پیشتر در متد Configure کلاس Startup انجام میشدند، الان همگی در تک فایل Program.cs قرار دارند:
var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection();
در انتهای این فایل نیز تعاریف پیشفرض زیر قرار دارند:
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); }
در همین حال اگر برنامهی Api را اجرا کنیم، به تصویر زیر خواهیم رسید:
در ادامه کدهای موجود در این فایل را Refactor کرده و به کلاسهای دیگری منتقل میکنیم؛ چون اگر قرار باشد در طول زمان تمام endpoints مدنظر را در همینجا تعریف کنیم، کنترل برنامه از دست خارج خواهد شد.
غنی سازی Solution و کامپایلر #C با استفاده از فایلهای editorconfig. و Directory.Build.props
در مورد این دو فایل در مطلب «غنی سازی کامپایلر C# 9.0 با افزونهها » بیشتر بحث شدهاست. هدف از آنها، اعمال یکسری تنظیمات سراسری، به تمام پروژههای یک solution به صورت یکدست است؛ مانند تنظیمات کامپایلر جهت نمایش اخطارها به صورت خطاها، تعریف usingهای سراسری سیشارپ 10 و یا اعمال Roslyn analyzers به تمام پروژهها. این دو فایل را به همراه پروژهی پیوست میتوانید دریافت کنید و ... باید جزء استاندارد تمام پروژههای جدید باشند. چون وجود آنها سبب خواهد شد که به شدت کیفیت کدهای نهایی افزایش یابند و مبتنی بر یکسری best practices شوند.
جواب ساده و کوتاه: خیر!
کدمدیریت شدهی شما در هر دو پلتفرم 32 بیتی - x86 و x64 بدون نیاز به هیچگونه تغییری و بدون نگرانی اجرا خواهد شد.
گزیدهای از MSDN :
اگر کد شما 100 درصد مدیریت شده است (managed code ایی که به صورت خالص از دات نت فریم ورک استفاده میکند و هیچگونه وابستگی خارجی دیگری به کتابخانههای دیگر ندارد)، تنها با کپی شدن در یک محیط x64 دارای CLR ایی 64 بیتی (دات نت فریم ورک 64 بیتی)، بدون هیچگونه مشکلی اجرا خواهد شد.
سؤال: چرا و چگونه؟!
کامپایلرهای دات نتی (تفاوتی نمیکند که چه زبانی مورد استفاده باشد)، کد شما را به IL ترجمه میکنند و IL اساسا درکی از پروسسور ندارد. JIT است که در آخرین لحظه در این مورد تصمیم گیری میکند.
این نگرانی از کجا حاصل شده است؟
نگارش R2 ویندوز 2008 سرور، فقط 64 بیتی خواهد بود و ویندوز سرور 2008 فعلی، آخرین سروری از مایکروسافت است که هر دو نسخهی 32 بیتی و 64 بیتی را دارد. بنابراین دیر یا زود تمام برنامه نویسهای ویندوزی "مجبور" خواهند شد دنیای 64 بیتی را تجربه کنند. (البته اگر تاکنون آنرا تجربه نکردهاند)
و البته هنوز یک سری از محیطهای توسعه، کامپایلر مخصوص 64 بیتی ندارند (مانند دلفی که قرار است در طول سال جاری اولین تجربهی 64 بیتی خود را ارائه دهد)
نکته:
در صفحهی build ویژوال استودیو، شما میتوانید نوع پلتفرم مورد نظر را نیز تعیین کنید:
پیش فرض آن بر روی Any CPU است و در این حالت کد کامپایل شدهی شما بدون مشکل بر روی پلتفرمهایی که مشاهده میکنید اجرا خواهد شد و تنها پیشنیاز اجرای آن، نصب نسخهی دات نت فریم ورک مخصوص آن پلتفرم است، بدون اینکه نیاز باشد برنامه نویس نگران جزئیات خاصی در مورد خصوصیات ویژهی آن پلتفرم ویژه باشد.
سؤال: اگر کد ما خالص نبود چطور؟ (منظور اینکه 100 درصد دات نتی نبود)
حالت الف) اگر از کامپوننتهای خارجی استفاده میکنید (حتی اگر 100 درصد دات نتی هم باشند) حتما اطمینان حاصل کنید که برای پلتفرم خاصی کامپایل نشدهاند (همان Any CPU مورد استفاده بوده)، زیرا کد شما که برای تمام CPU ها کامپایل شده، در محیط 64 بیتی، تنها توانایی بارگذاری اسمبلیهای 64 بیتی را خواهد داشت (64 بیتی رفتار میکند) و با مواجه شدن با اسمبلیهایی که برای یک پروسسور خاص دیگر کامپایل شدهاند، با خطای BadImageFormatException خاتمه مییابد.
حالت ب) استفاده از API ویندوز یا DLL های غیر دات نتی
باید با هماهنگی با تولید کنندهی مربوطه حتما از نگارش 64 بیتی استفاده شود و همچنین برنامهی شما باید توانایی استفاده از اشارهگرهای 64 بیتی را داشته باشد. اندازهی نوع دادهای IntPtr در یک محیط 32 بیتی 4 است و در یک محیط 64 بیتی 8 خواهد بود (IntPtr.Size). اگر در حین اجرای ترجمهی API یک کتابخانه به اشتباه بجای استفاده از IntPtr از int استفاده شده باشد، ممکن است کد شما در یک محیط 32 بیتی سالها بدون مشکل اجرا شود، اما در اولین اجرای خود در یک محیط 64 بیتی، کرش خواهد کرد. (بدلیل overflow حاصل)
IntPtr به اندازهی کافی هوشمند است تا سایز خودش را مطابق پلتفرم تنظیم کند و مشکل ساز نشود.
مثال:
[DllImport("kernel32.dll")]
public static extern void GetSystemInfo([MarshalAs(UnmanagedType.Struct)] ref SYSTEM_INFO lpSystemInfo);
[StructLayout(LayoutKind.Sequential)]
public struct SYSTEM_INFO
{
internal _PROCESSOR_INFO_UNION uProcessorInfo;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public int lpMaximumApplicationAddress;
public IntPtr dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public ushort dwProcessorLevel;
public ushort dwProcessorRevision;
}
[StructLayout(LayoutKind.Explicit)]
public struct _PROCESSOR_INFO_UNION
{
[FieldOffset(0)]
internal uint dwOemId;
[FieldOffset(0)]
internal ushort wProcessorArchitecture;
[FieldOffset(2)]
internal ushort wReserved;
}
با توجه به افزایش کاربرد jQuery و دیگر کتابخانههای جاوا اسکریپت در برنامههای تحت وب، یکی از چالشهای همیشگی برنامه نویسان، فشرده سازی فایلهای دربرگیرنده کدهای جاوا اسکریپت و شیوه نامهها می باشد. برای این منظور راههای مختلفی مانند استفاده از ابزارهای آنلاین مانند این + و این + وجود دارند. اما یک روش خودکار هم وجود دارد که در زمان Build پروژههای دات نت میتوان از آن بهره گرفت.
Microsoft Ajax Minifier
یک ابزار رایگان جهت فشرده سازی فایلهای جاوا اسکریپت و شیوه نامهها است. شما میتوانید این ابزار را از صفحه خانگی آن در سایت asp.net دریافت کنید. جهت استفاده از این ابزار میتوان از طریق خط فرمان عمل کرد. اما روش سادهتر که هدف اصلی این مطلب است به شرح زیر است:
1. در VisualStudio.NET از طریق منو به مسیر Tools, Options, Projects and Solutions بروید و گزینه Always show solution را تیک بزنید.
2. از Solution Explorer بر روی عنوان پروژه کلیک راست کرده و گزینه Unload Project را انتخاب نمایید.
3. مجدداً روی عنوان پروژه کلیک راست کرده و گزینه Edit را انتخاب کنید و دستورات زیر را قبل از بسته شدن تگ Project اضافه کنید:
<Import Project="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\ajaxmin.tasks" /> <Target Name="AfterBuild"> <ItemGroup> <JS Include="**\*.js" Exclude="**\*.min.js;Scripts\*.js" /> </ItemGroup> <ItemGroup> <CSS Include="**\*.css" Exclude="**\*.min.css" /> </ItemGroup> <AjaxMin JsSourceFiles="@(JS)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".min.js" CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".min.css" /> </Target>
4. دوباره بر روی عنوان پروژه کلیک راست کرده و گزینه Reload Project را انتخاب کنید.
توجه کنید که با این کار ما یک MSBuild task با عنوان ajaxmini به پروژه اضافه کردیم. این وظیفه که در زمان Build پروژه اجرا خواهد شد فایلهای جاوا اسکریپت را فشرده و با پسوند .min.js و همچنین فایلهای CSS را پس از فشرده سازی با پسوند .min.css در همان مسیر فایل مادر بطور خودکار ذخیره میکند.
نکته:
اگر به دستورات تنظیمات فوق نگاه دقیقتری بیندازیم، متوجه عبارات Include و Exclude می شویم. توسط این دو صفت شما میتوانید الگوهایی را جهت فشرده سازی و یا عدم فشرده سازی تعریف کنید. بدین معنا که توسط الگویهای ذکر شده در تنظیمات فوق از فشرده سازی فایلهای با پسوند .min.css و .min.js خودداری میشود.
در این شرایط در حین توسعه برنامه، شما میتوانید از فایلهای با کد خوانا استفاده نمایید و زمان انتشار و Build پروژه بصورت خودکار آنها را با فایلهای فشرده جایگزین کنید.
این ابزار تمامی فضاهای خالی، ';' و '{ }'های اضافی و توضیحات را از کدهای شما حذف میکند. متغیرها و توابع شما را به اسامی کوجکتر تغییر نام میدهد. و ...
همچنین شما از کتابخانه این پروژه میتوانید در زمان اجرا و سورس برنامه خود استفاده کنید. جهت اطلاعات بیشتر میتوانید به سایت مربوطه مراجعه نمایید.
نظرات اشتراکها
پایان کار Inoreader برای کاربران عادی
سلام
مورد مناسبی با دات نت پیاده سازی شده؟از موارد غیر دات نتی موردی رو میشناسید که بدون دردسر بشه روی یک سرور ویندوزی راه انداخت؟
اشتراکها
اجرای ورد پرس با کمک .net core
سؤال مربوط به حالت نخ نمای Page.IsPostBack نیست. مربوط به حالتی است که دقیقا در اولین بار مشاهدهی عادی یک صفحه، Page_Load دوبار یا بیشتر (!) اجرا میشود.
الف) برنامهی ASP.Net 1.x خود را به نگارشهای 2+ ارتقاء دادهاید.
در این حالت هر چند VS.Net پیغام تبدیل با موفقیت یک پروژهی قدیمی را به شما ارائه خواهد داد اما یک سری موارد را پس از تبدیل، باید اصلاح کرد.
پروژههای قدیمی ASP.Net در روال InitializeComponent خود سطر زیر را همانند یک پروژه WinForm و امثال آن برای معرفی روال رخداد گردان Page_Load دارند.
this.Load += new System.EventHandler(this.Page_Load);
اما در پروژههای ASP.Net 2.0 به بعد دیگر از این روال خبری نیست. پس در این پروژهها کامپایلر چگونه متوجه خواهد شد که Page_Load واقعا یک روال رخ داد گردان است و نه یک روال معمولی؟ در پروژههای جدید VS.Net ، خاصیت AutoEventWireup صفحه به true تنظیم شده و دیگر نیازی به معرفی صریح روالهای رخ داد گردانی مانند Page_Load و یا Page_Init نبوده و تشخیص آنها به صورت خودکار انجام میشود.
<% @Page AutoEventWireup="true" %>
برای حل این مشکل و سازگاری بهتر با نگارشهای جدیدتر، سطر تعریف دستی روال رخ داد گردان متد Page_Load را حذف کنید.
لازم به ذکر است که این سیم کشی خودکار تنها برای متدهای زیر انجام خواهد شد و نسبت به حذف سایر موارد موجود اقدام نکنید!
Page_PreInit
Page_Init
Page_InitComplete
Page_PreLoad
Page_Load
Page_LoadComplete
Page_DataBind
Page_SaveStateComplete
Page_PreRender
Page_PreRenderComplete
Page_Unload
Page_Error
Page_AbortTransaction
Page_CommitTransaction
ب) وجود هر نوع دکمهی تصویری یا کلا تصویری با ویژگی src مقدار دهی نشده در صفحه
مرورگر IE با این مساله مشکلی ندارد. اما فایرفاکسهای جدید اگر به src مقدار دهی نشدهی تصویری برخورد کنند دقیقا آدرس جاری صفحه را بجای مقدار src قرار داده و مجددا صفحه را درخواست میکنند (و البته این مورد ارتباط مستقیمی به ASP.Net یا PHP و امثال آن ندارد و یک مسالهی عمومی است). این مورد سبب خواهد شد که Page_Load صفحه، نه فقط دوبار بلکه به تعداد بار src خالی تصاویری که در صفحه وجود دارند، بر اساس درخواستهای مجدد فایرفاکس از سرور اجرا شود. (مرورگر IE بجای فراخوانی آدرس صفحه جاری، یک null/ را به انتهای آدرس جاری اضافه کرده و آنرا فراخوانی میکند. بنابراین سبب اجرای مجدد هیچ روالی نخواهد شد.)
مآخذ:
Inside AutoEventWireup
How Firefox Handles Empty SRC tags
نوعهای Generics در دات نت از اعمال ریاضی مانند جمع و ضرب و منها پشتیبانی نمیکنند. در مقالات ذیل راه حلهایی برای رفع این مشکل ارائه شدهاند که میتوانند تبدیل به یک مقاله جدید گردند:
بازخوردهای دوره
استفاده از StructureMap به عنوان یک IoC Container
مشکل تداخل نگارشهای مختلف را با هم دارید.
- ابتدا مطمئن شوید که بستهها را فقط از طریق نیوگت اضافه کردهاید و نه ارجاع دستی (مهم).
- همچنین مطمئن شوید که بستهی EF در پروژهای که به آن اشاره کرده، نصب شدهاست.
- سپس یکبار تمام بستهها را به روز کنید و در ادامه یکبار هم بر اساس نگارش فعلی دات نت انتخابی، آنها را تنظیم مجدد کنید:
- ابتدا مطمئن شوید که بستهها را فقط از طریق نیوگت اضافه کردهاید و نه ارجاع دستی (مهم).
- همچنین مطمئن شوید که بستهی EF در پروژهای که به آن اشاره کرده، نصب شدهاست.
- سپس یکبار تمام بستهها را به روز کنید و در ادامه یکبار هم بر اساس نگارش فعلی دات نت انتخابی، آنها را تنظیم مجدد کنید:
PM> update-package PM> update-package -reinstall
در دنیای وب ویرایشگرهای متنوعی موجود هستند اما دو ویرایشگر مطرح CKEditor , TinyMCE سهم زیادی را به خود اختصاص داده است . عمده مشکل توسعه دهندگان وب هم مدیریت فایل و آپلود میباشد. یکی از مزایای این ابزار ، محدود کردن مسیر پایه برای بخش آپلود و ... میباشد.
هم برای زبان دات نت و هم برای زبان php هم موجود است.