در ادامه قصد داریم تمام این موارد را مانند 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 شوند.
// Disable all menus tinymce.init({ menubar : false }); // Configure different order tinymce.init({ menubar: "tools table format view insert edit" });
CKEditor
Tinymce
در نسخههای پیشین tiny این دیالوگها به صورت پنجرههای popup باز میشدند، ولی در تمامی نسخههای ck بسیاری از این پنجرهها بدین صورت باز نشده و سفارشی هستند. درست است که در ظاهر مثل هم باز میشوند ولی مکانیزم و روش باز شدن آنها با یکدیگر متفاوت است و در واقع ck با روشی هوشمندانه و با استفاده از کدهای جاوا اسکریپت و ترکیب لایههای html این روش را پیاده سازی میکند.
شاید بگویید خوب چه فرقی میکند که از چه روشی برای پیاده سازی استفاده شود؟ این مورد از دو جنبه مورد بررسی قرار میگیرد:
1 - اگر به مرورگرهایی چون IE دقت کرده باشید، میبینید که موقعیکه یک popup باز میشود، به عنوان یک پنجرهی جدید باز میشود و باعث شلوغ شدن صفحه میگردد و بیشتر موجب آزار کاربر میگردد.
2- ساخت واقعی یک صفحه popup منجر شدن به ایجاد یک پنجره ای جدید و طی شدن فرآیندهای مربوط به بارگذاری و رندر شدن یک صفحهی جدید میباشد؛ ولی در مورد پنجرههای سفارشی این فرآیندها صورت نمیگیرد. این مورد بر روی بسیاری از هاستها خود را به وضوح نمایش میدهد و ck در صرفه جویی از زمان، به مراتب بهتر عمل میکند.
از نسخه tinymce 4 به بعد مکانیزم سفارشی بودن دیالوگها بر روی این ادیتور نیز صورت گرفته است.
کپی و پیست Copy - Paste
بسیاری از کاربران گاهی اوقات مطالب خود را از جایی یا از یک صفحهی وب دیگر به ادیتور کپی میکنند. برای مثال در اینجا صفحهی گوگل را به داخل ادیتورها کپی میکنیم و به نظر میرسد ckeditor در این مواقع رفتار بهتری نسبت به tiny از خود نشان میدهد.
در تاینی لوگوی گوگل دقیقا در وسط قرار گرفته و بعضی موارد خاص مانند فرمها به خوبی نمایش داده میشوند؛ ولی در ck شما متن تمیزتری دارید و طوری نشان داده میشود که یک css کاربرپسند به آن اضافه شده است.
Ckeditor
انعطاف پذیری Flexibility
هر دو ادیتور در این زمینه به خوبی طراحی شدهاند و با استفاده از جاوا اسکریپت میتوان آنها را به سادگی توسعه داد و امکانات دلخواهتان را به آن اضافه کنید ولی در کل ckeditor در زمینه apiهای برنامه نویسی، پیشروتر از رقیبش tinymce است.
حجم یا سایز ادیتورها Size
با توجه به رشد اینترنت در گوشیهای همراه و دیگر وسایل همراه، حجم منابع اینترنتی بیش از قبل مورد توجه قرار میگیرد. در این زمینه tinymce برگ برندهای داشته و بهتر است بدانید که حجم این ویرایشگر تقریبا نصف رقیبش یعنی ckeditor میباشد.
حالا شما با توجه به موارد بالا میتوانید ادیتور خود را انتخاب کنید.
در اینجا هم میتوانید فایلهای مربوط به زبان را نیز دانلود کنید که هر دو از زیان فارسی پشتیبانی میکنند.
منابع:
Let's start our journey together to build beautiful native cross-platform apps for iOS, Android, macOS, and Windows with .NET MAUI, C#, and Visual Studio! In this full workshop, I will walk you through everything you need to know about .NET MAUI and building your very first app. You will learn the basics including how to build user interfaces with XAML, how MVVM and data binding simplify development, how to navigate between pages, access platform features like geolocation, optimize data collections, and theme your app for light theme and dark theme. This course has everything you need to learn the basics and set you up for success when building apps with .NET MAUI!
Chapters:
00:00:00 - Intro to the .NET MAUI Workshop
00:04:10 - What is .NET MAUI & How to Install
00:06:25 - Workshop overview
00:08:00 - First .NET MAUI app & Architecture (slides)
00:21:40 - Get code to build your first .NET MAUI app
00:25:00 - .NET MAUI Project Walkthrough
00:29:40 - Start to build first .NET MAUI app
00:56:48 - Intro to MVVM (slides)
01:09:30 - Implementing INotifyPropertyChanged
01:22:30 - .NET Community Toolkit for MVVM (Source Generators)
01:32:30 - HTTP REST Calls & JSON Deserialization
01:43:00 - ICommand in .NET MAUI
01:59:30 - Create our UI with XAML & MVVM
02:16:20 - Navigation in .NET MAUI (slides)
02:25:20 - Implementing Navigation in .NET MAUI & Passing Parameters
02:46:00 - Building a details UI with XAML & MVVM
02:54:10 - Modal, Back Navigation, & More
02:58:20 - Access Platform APIs in .NET MAUI (slides)
03:02:53 - Platform API Integration - Connectivity
03:08:30 - Geolocation & Permissions Implementation
03:18:50 - Open Map Integration
03:22:40 - Platform Specifics - iOS Safe Area
03:25:50 - CollectionView & RefreshView Overview (slides)
03:34:00 - Implementing Pull-to-Refresh
03:40:00 - CollectionView Layouts - Grids and more
03:41:30 - CollectionView EmptyView
03:45:00 - App Resources, Styles, and Themes (slides)
03:56:20 - Implementing Light & Dark Mode
04:06:00 - Thanks, wrap-up, and resources
ایجاد پروژهی ASP.NET Core Web API
برای تامین اطلاعات برنامهی سمت کلاینت Blazor WASM و همچنین فراهم آوردن زیرساخت اعتبارسنجی کاربران آن، نیاز به یک پروژهی ASP.NET Core Web API داریم که آنرا با اجرای دستور dotnet new webapi در یک پوشهی خالی، برای مثال به نام BlazorWasm.WebApi ایجاد میکنیم.
البته این پروژه، از زیرساختی که در برنامهی Blazor Server بررسی شدهی تا این قسمت، ایجاد کردیم نیز استفاده خواهد کرد. همانطور که پیشتر نیز عنوان شد، هدف از قسمت Blazor Server مثال این سری، آشنایی با مدل برنامه نویسی خاص آن بود؛ وگرنه میتوان کل این پروژه را با Blazor Server و یا کل آنرا با Web API + Blazor WASM نیز پیاده سازی کرد. در این مثال، قسمتهای مدیریتی برنامهی مدیریت هتل را توسط Blazor Server (مانند قسمتهای تعریف اتاقها و امکانات رفاهی هتل) و قسمت مخصوص کاربران آنرا مانند رزرو کردن اتاقها، توسط Blazor WASM پیاده سازی میکنیم. به همین جهت قسمتهایی از این دو پروژه، مانند سرویسهای استفاده شدهی در پروژهی Blazor server، در پروژهی Web API مکمل Blazor WASM، قابلیت استفادهی مجدد را دارند.
افزودن سرویسهای آغازین مورد نیاز، به پروژهی Web API
در فایل آغازین BlazorWasm\BlazorWasm.WebApi\Startup.cs، برای شروع به تکمیل Web API، نیاز به این سرویسها را داریم:
namespace BlazorWasm.WebApi { public class Startup { //... public void ConfigureServices(IServiceCollection services) { services.AddAutoMapper(typeof(MappingProfile).Assembly); services.AddScoped<IHotelRoomService, HotelRoomService>(); services.AddScoped<IAmenityService, AmenityService>(); services.AddScoped<IHotelRoomImageService, HotelRoomImageService>(); var connectionString = Configuration.GetConnectionString("DefaultConnection"); services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<ApplicationDbContext>() .AddDefaultTokenProviders(); //...
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" /> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.6.3" /> </ItemGroup> <ItemGroup> <ProjectReference Include="..\..\BlazorServer\BlazorServer.DataAccess\BlazorServer.DataAccess.csproj" /> <ProjectReference Include="..\..\BlazorServer\BlazorServer.Services\BlazorServer.Services.csproj" /> <ProjectReference Include="..\..\BlazorServer\BlazorServer.Models.Mappings\BlazorServer.Models.Mappings.csproj" /> </ItemGroup> </Project>
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=HotelManagement;Trusted_Connection=True;MultipleActiveResultSets=true" } }
تعریف کنترلر HotelRoom
در ادامه کدهای اولین کنترلر Web API را مشاهده میکنید که مرتبط است با بازگشت اطلاعات تمام اتاقهای ثبت شده و یا بازگشت اطلاعات یک اتاق ثبت شده:
using BlazorServer.Models; using BlazorServer.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading.Tasks; namespace BlazorWasm.WebApi.Controllers { [Route("api/[controller]")] public class HotelRoomController : ControllerBase { private readonly IHotelRoomService _hotelRoomService; public HotelRoomController(IHotelRoomService hotelRoomService) { _hotelRoomService = hotelRoomService; } [HttpGet] public IAsyncEnumerable<HotelRoomDTO> GetHotelRooms() { return _hotelRoomService.GetAllHotelRoomsAsync(); } [HttpGet("{roomId}")] public async Task<IActionResult> GetHotelRoom(int? roomId) { if (roomId == null) { return BadRequest(new ErrorModel { Title = "", ErrorMessage = "Invalid Room Id", StatusCode = StatusCodes.Status400BadRequest }); } var roomDetails = await _hotelRoomService.GetHotelRoomAsync(roomId.Value); if (roomDetails == null) { return BadRequest(new ErrorModel { Title = "", ErrorMessage = "Invalid Room Id", StatusCode = StatusCodes.Status404NotFound }); } return Ok(roomDetails); } } }
- ErrorModel آنرا در همان پروژهی قبلی مدلها، در فایل BlazorServer\BlazorServer.Models\ErrorModel.cs به صورت زیر ایجاد کردهایم:
namespace BlazorServer.Models { public class ErrorModel { public string Title { get; set; } public int StatusCode { get; set; } public string ErrorMessage { get; set; } } }
یکی از مزایای آن، امکان آزمایش API تهیه شده، بدون نیاز به تهیهی هیچ نوع کلاینت خاصی است. برای مثال اگر بر روی api/hotelroom آن کلیک کنیم، گزینهی «try it out» آن ظاهر شده و با کلیک بر روی آن، اینبار دکمهی execute ظاهر میشود. در ادامه با کلیک بر روی دکمهی اجرای آن، اکشن متد GetHotelRooms اجرا شده و خروجی زیر ظاهر میشود:
و یا اگر بخواهیم متد GetHotelRoom را توسط آن آزمایش کنیم، بر اساس پارامترهای آن، رابط کاربری زیر را تشکیل میدهد که امکان دریافت شمارهی اتاق را دارد:
انجام تنظیمات ابتدایی CORS و خروجی JSON برنامه
قرار است این API را از طریق پروژهی Blazor سمت کلاینت خود استفاده کنیم که آدرس آن، با آدرس API یکی نیست. به همین جهت نیاز است تنظیمات CORS را به صورت زیر اضافه کنیم:
namespace BlazorWasm.WebApi { public class Startup { public void ConfigureServices(IServiceCollection services) { // ... services.AddCors(o => o.AddPolicy("HotelManagement", builder => { builder.AllowAnyOrigin().AllowAnyMethod().AllowAnyHeader(); })); services.AddControllers() .AddJsonOptions(options => { options.JsonSerializerOptions.PropertyNamingPolicy = null; // To avoid `JsonSerializationException: Self referencing loop detected error` options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve; }); // ... } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // ... app.UseCors("HotelManagement"); app.UseRouting(); app.UseAuthentication(); // ... } } }
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-24.zip
These are the customer-reported issues addressed in 15.7.3:
- VS2017 compiler creates broken debug build using Qt framework and generates 'Invalid address specified to RtlValidateHeap' error.
- Incorrect code generation for matrix multiplication.
- VS 2017 Update 7: Git History Codelens only showing entries for the past 6 months.
- UWP projects reference multiple NetStandard 2.0 dlls after 15.7.1 upgrade.
- Building C++ code in VS 15.7 with /std:c++17 breaks binary compatibility for std::_Ptr_move_cat.
- Visual Studio 15.7 stuck when opening XAML files.
- CMake configuration fails and generates message "C++ IntelliSense information may be out of date, generate the CMake cache to refresh".
- Unable to start second process for debugging.
- After update to Visual Studio 15.7.1, some test programs fail in start-up due to reading access violation.
- Missing compiler required member 'Microsoft.CSharp.RuntimeBinder.Binder.Convert'.
- Build fails after 15.7.0 update on older project using .NET 3.5 .
- Coloring, typing, tooltips and IntelliSense slow in F# in VS2017 editor.
- F# editing experience takes up to a minute for tooltips and dropdowns to display.
- Certain class member variable value are incorrectly read as zero.
- Attempt to open XAML file for the first time causes VS to sit with the "Opening the file ..." message for about 10 minutes before XAML file opens.
- Visual Studio slows down and freezes, creating work loss.
- The target "GetBuiltProjectOutputRecursive" does not exist in the project.
- Internal error with lambda C++17 after 15.7.1 update.
- UWP App is slow to return a stopped state in IDE.
- MSVC auto-vectorization produces incorrect code or incorrect results.
- Visual Studio closed debug a new instance project1, when a new debug new instance project2 has started.
- Latest update breaks "start without debugging" on multiple projects.
- UWP XAML is very very slow on open.
- XAML viewer freezes on 15.7.2 and 15.8.0 preview 1.1.
- Xamarin UI Test App project template missing.
- Xamarin project creation problem.
- Visual Studio crashes when creating new Mobile APP Xamarin.
- Unable to create Xamarin.Forms-Projects.
- Não consigo criar novos projetos Xamarin Forms - Can't create new projects Xamarin Forms.
- Blank project crash after update.
احراز هویت و اعتبارسنجی کاربران در برنامههای Angular - قسمت اول - معرفی و ایجاد ساختار برنامه
dotnet new angular --auth Individual
dotnet new react --auth Individual //or dotnet new reactredux --auth Individual
فرض کنید به یک سرور مراجعه کردهاید و شکایت از CPU Usage مربوط به پروسه w3wp.exe یا همان IIS Worker Process است که بالای 90 درصد میباشد. بر روی این سرور هم هیچ چیز دیگری نصب نیست و مطابق مقررات موجود، قرار هم نیست که برنامهای نصب شود. اکنون سؤال این است که چطور تشخیص میدهید، کدام قسمت یکی از برنامههای دات نتی در حال اجرا (در اینجا یکی از برنامههای ASP.NET هاست شده)، سبب بروز این مشکل شده است؟ کدام ترد بیشترین زمان CPU را به خود اختصاص داده است؟ چطور باید خطایابی کرد؟
اولین کاری که در این موارد توصیه میشود مراجعه به برنامهی معروف process explorer و بررسی برگهی threads آن است. در اینجا حتی میتوان call stacks مرتبط با یک ترد را هم مشاهده کرد. اما ... این برگه در مورد پروسهها و تردهای دات نتی، اطلاعات چندانی را در اختیار ما قرار نمیدهد.
خوشبختانه امکان دیباگ پروسههای دات نتی در حال اجرا توسط کتابخانهی MdbgCore.dll پیش بینی شده است. این فایل را در یکی از مسیرهای ذیل میتوانید پیدا کنید:
C:\Program Files\Microsoft SDKs\Windows\vXYZ\bin\MdbgCore.dll
C:\Program Files\Microsoft SDKs\Windows\vXYZ\bin\NETFX 4.0 Tools\MdbgCore.dll
در ادامه میخواهیم توسط امکانات این کتابخانه، به stack trace تردهای در حال اجرای یک برنامه دات نتی دسترسی پیدا کرده و سپس نام متدهای مرتبط را نمایش دهیم:
using System;
using System.Collections;
using System.Diagnostics;
using Microsoft.Samples.Debugging.MdbgEngine;
namespace CpuAnalyzer
{
class Program
{
static void Main(string[] args)
{
var engine = new MDbgEngine();
var processesByName = Process.GetProcessesByName("MyApp");
if (processesByName.Length == 0)
throw new InvalidOperationException("specified process not found.");
var processId = processesByName[0].Id;
var process = engine.Attach(processId);
process.Go().WaitOne();
foreach (MDbgThread thread in (IEnumerable)process.Threads)
{
foreach (MDbgFrame frame in thread.Frames)
{
if (frame == null || frame.Function == null) continue;
Console.WriteLine(frame.Function.FullName);
}
}
process.Detach().WaitOne();
}
}
}
خوب در مرحله بعد شاید بد نباشد که این متدها را بر اساس CPU usage آنها مرتب کنیم. به این ترتیب بهتر میتوان تشخیص داد که کدام متد مشکل ساز بوده است. برای این منظور باید به API ویندوز و تابع GetThreadTimes مراجعه کرد و اولین پارامتر ورودی آن، همان thread.CorThread.Handle اولین حلقه مثال فوق میباشد. هر کدام که جمع KernelTime + UserTime بیشتری داشت، همان است که مشکل درست کرده است.
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool GetThreadTimes(IntPtr handle, out long creation, out long exit, out long kernel, out long user);
for (int i = 0; i < 10; i++)
{
foreach (MDbgThread thread in (IEnumerable)process.Threads)
{
//...
}
process.Go();
Thread.Sleep(1000);
process.AsyncStop().WaitOne();
}
در کل این مثال جای کار زیاد دارد. برای مثال طراحی یک رابط کاربری برای آن و نمایش جزئیات بیشتر. به همین منظور حداقل سه پروژه مشابه را میتوان نام برد: