اشتراکها
عرضه TypeScript 1.0RC
اشتراکها
Visual Studio 2013.1 RC منتشر شد!
در ادامه قصد داریم یک پروژهی مدیریت هتل را پیاده سازی کنیم. این پروژه، دو قسمتی است. قسمت اول آن یک پروژهی Blazor Server، برای مدیریت هتل مانند تعاریف اتاقها است و پروژهی دوم آن از نوع Blazor WASM، برای مراجعهی کاربران عمومی و رزرو اتاقها است. هدف، بررسی نحوهی کار با هر دو نوع فناوری است. وگرنه میتوان کل پروژه را با Blazor Server و یا کل آنرا با Blazor WASM هم پیاده سازی کرد. در مورد نحوهی انتخاب و مزایا و معایب هرکدام از این فناوریها، در قسمتهای اول و دوم این سری بیشتر بحث شدهاست.
ساختار پوشهها و پروژههای قسمت Blazor Server
قسمت Blazor Server مدیریت هتل ما از 7 پروژه و پوشهی زیر تشکیل میشود:
- BlazorServer.App: پروژهی اصلی Blazor Server است که با اجرای دستور dotnet new blazorserver در پوشهی خالی آن آغاز میشود.
- BlazorServer.Common: پروژهای از نوع classlib، جهت قرارگیری کدهای مشترک بین پروژهها است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.DataAccess: پروژهای از نوع classlib، برای تعریف DbContext برنامه است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Entities: پروژهای از نوع classlib، جهت تعریف کلاسهای متناظر با جداول بانک اطلاعاتی برنامه است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Models: پروژهای از نوع classlib، برای تعریف کلاسهای data transfer objects برنامه (DTO's) است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Models.Mappings: پروژهای از نوع classlib، برای تعریف نگاشتهای بین DTO's و مجودیتهای برنامه و برعکس است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Services: پروژهای از نوع classlib، جهت تعریف کدهایی که منطق تجاری تعامل با بانک اطلاعاتی را از طریق BlazorServer.DataAccess میسر میکند که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
اصلاح پروژهی BlazorServer.App جهت استفاده از LibMan
قالب پیشفرض BlazorServer.App، به همراه پوشهی wwwroot\css است که در آن بوت استرپ و open-iconic به همراه فایل site.css قرار دارند. چون این پروژه به همراه هیچ نوع روشی برای مدیریت نگهداری بستههای سمت کلاینت خود نیست، دو پوشهی بوت استرپ و open-iconic آنرا حذف کرده و از روش مطرح شدهی در مطلب «Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت» استفاده خواهیم کرد:
در پوشهی ریشهی پروژهی BlazorServer.App، دستورات فوق را اجرا میکنیم تا بستههای bootstrap و open-iconic را در پوشهی wwwroot/lib نصب کند و همچنین فایل libman.json متناظری را نیز جهت اجرای دستور libman restore برای دفعات آتی، تولید کند.
بعد از نصب بستههای ذکر شده، ابتدا سطر زیر را از ابتدای فایل پیشفرض wwwroot\css\site.css حذف میکنیم:
سپس فایل Pages\_Host.cshtml را به صورت زیر به روز رسانی میکنیم تا به مسیرهای جدید بستههای CSS، اشاره کند:
برای bundling & minification این فایلها میتوان از «Bundler Minifier» استفاده کرد.
پروژهی موجودیتهای مدیریت هتل
فایل BlazorServer.Entities.csproj وابستگی خاصی را نداشته و به صورت زیر تعریف شدهاست:
در این پروژه، کلاس جدید HotelRoom را که بیانگر ساختار جدول متناظری در بانک اطلاعاتی است، به صورت زیر تعریف میکنیم:
که شامل فیلدهایی مانند نام، ظرفیت، مساحت و ... یک اتاق هتل است.
پروژهی تعریف DbContext برنامهی مدیریت هتل
فایل BlazorServer.DataAccess.csproj به این صورت تعریف شدهاست:
- در اینجا چون نیاز است موجودیت HotelRoom را به صورت یک DbSet معرفی کنیم، ارجاعی را به پروژهی BlazorServer.Entities.csproj تعریف کردهایم.
- همچنین دو وابستگی مورد نیاز جهت کار با EntityFrameworkCore و اجرای مهاجرتها را نیز به آن افزودهایم.
پس از تامین این وابستگیها، اکنون میتوان DbContext ابتدایی برنامه را به صورت زیر تعریف کرد که کار آن، در معرض دید قرار دادن HotelRoom به صورت یک DbSet است:
پس از این مرحله، نیاز است این DbContext را به سیستم تزریق وابستگیهای برنامهی اصلی معرفی کرد. بنابراین فایل BlazorServer.App.csproj پروژهی اصلی Blazor Server را گشوده و تغییرات زیر را اعمال میکنیم:
- چون میخواهیم ApplicationDbContext را به سیستم تزریق وابستگیها معرفی کنیم، بنابراین باید بتوان به کلاس آن نیز دسترسی داشت که اینکار، با تعریف ارجاعی به BlazorServer.DataAccess.csproj میسر شدهاست.
- سپس چون میخواهیم از تامین کنندهی بانک اطلاعاتی SQL Server نیز استفاده کنیم، وابستگیهای آنرا نیز افزودهایم.
با این تنظیمات، به فایل BlazorServer\BlazorServer.App\Startup.cs مراجعه کرده و کار افزودن AddDbContext و UseSqlServer را انجام میدهیم تا DbContext برنامه از طریق تزریق وابستگیها قابل دسترسی شود و همچنین رشتهی اتصالی مشخص شده نیز به تامین کنندهی SQL Server ارسال شود:
این رشتهی اتصالی را به صورت زیر در فایل BlazorServer\BlazorServer.App\appsettings.json تعریف کردهایم:
که در حقیقت یک رشتهی اتصالی جهت کار با LocalDB است.
اجرای مهاجرتها و تشکیل ساختار بانک اطلاعاتی
پس از این تنظیمات، اکنون میتوانیم به پوشهی BlazorServer\BlazorServer.DataAccess مراجعه کرده و از طریق خط فرمان، دستورات زیر را صادر کنیم:
- در ابتدا نیاز است ابزارهای مهاجرت EF-Core را نصب کنیم که سطر اول، اینکار را انجام میدهد.
- همیشه بهتر است پیش از اجرای عملیات Migration، یکبار dotnet build را اجرا کرد؛ تا اگر خطایی وجود دارد، بتوان جزئیات دقیق آنرا مشاهده کرد. چون عموما این جزئیات در حین اجرای دستورات بعدی، با پیام مختصر «عملیات شکست خورد»، نمایش داده نمیشوند.
- دستور سوم، کار تشکیل پوشهی BlazorServer\BlazorServer.DataAccess\Migrations و تولید خودکار دستورات تشکیل بانک اطلاعاتی را بر اساس ساختار DbContext برنامه انجام میدهد.
- دستور چهارم، بر اساس اطلاعات موجود در پوشهی BlazorServer\BlazorServer.DataAccess\Migrations، بانک اطلاعاتی واقعی را تولید میکند.
در این دستورات ذکر پروژهی آغازین برنامه جهت یافتن وابستگیهای پروژه ضروری است.
تکمیل پروژهی DTOهای برنامه
همواره توصیه شدهاست که موجودیتهای برنامه را مستقیما در معرض دید UI قرار ندهید. حداقل مشکلی را که در اینجا ممکن است مشاهده کنید، حملات از نوع mass assignment هستند. برای مثال قرار است از کاربر، کلمهی عبور جدید آنرا دریافت کنید، ولی چون اطلاعات دریافتی، به اصل موجودیت متناظر با بانک اطلاعاتی نگاشت میشود، کاربر میتواند فیلد IsAdmin را هم خودش مقدار دهی کند! و چون سیستم Binding بسیار پیشرفته عمل میکند، این ورودی را معتبر یافته و در اینجا علاوه بر به روز رسانی کلمهی عبور، خواص دیگری را هم که نباید به روز رسانی شوند، به روز رسانی میکند و یا در بسیاری از موارد نیاز است data annotations خاصی را برای فیلدها تعریف کرد که ربطی به موجودیت اصلی ندارند و یا نیاز است فیلدهایی را در UI قرار داد که باز هم تناظر یک به یکی با موجودیت اصلی ندارند (گاهی کمتر و گاهی بیشتر هستند و باید بر روی آنها محاسباتی صورت گیرد تا قابلیت ذخیره سازی در بانک اطلاعاتی را پیدا کنند). به همین جهت کار مدل سازی UI و یا بازگشت اطلاعات نهایی از سرویسها را توسط DTOها که یک سری کلاس سادهی C# 9.0 از نوع record هستند، انجام میدهیم:
Recordهای C# 9.0، انتخاب بسیار مناسبی برای تعریف DTOها هستند. از این لحاظ که قرار نیست اطلاعات دریافتی از کاربر، در این بین و پس از مقدار دهی اولیه، تغییر کنند.
در اینجا فیلدهای UI برنامه را که در قسمت بعد تکمیل خواهیم کرد، مشاهده میکنید؛ به همراه یک سری data annotation برای تعریف اجباری و یا بازهی مورد قبول، به همراه پیامهای خطای مرتبط.
نگاشت DTOهای برنامه به موجودیتها و بر عکس
یا میتوان خواص DTO تعریف شده را یکی یکی به موجودیتی متناظر با آن انتساب داد و یا میتوان از AutoMapper برای اینکار استفاده کرد. به همین جهت به BlazorServer.Models.Mappings.csproj مراجعه کرده و تغییرات زیر را اعمال میکنیم:
- پروژهای که کار تعریف نگاشتها را انجام میدهد، نیاز به اطلاعات موجودیتها و مدلها (DTO ها)ی متناظر را دارد. به همین جهت ارجاعاتی را به این دو پروژه، تعریف کردهایم.
- همچنین بستهی مخصوص AutoMapper را که به همراه امکانات تزریق وابستگیهای آن نیز هست، در اینجا افزودهایم.
پس از افزودن این ارجاعات، نگاشت دو طرفهی بین مدل و موجودیت تعریف شده را به صورت زیر تعریف میکنیم:
اکنون برای شناسایی پروفایل فوق و معرفی آن به AutoMapper، به فایل BlazorServer\BlazorServer.App\Startup.cs مراجعه کرده و تزریق وابستگی و ردیابی خودکار آنرا اضافه میکنیم که شامل اسکن تمام اسمبلیهای موجود، جهت یافتن Profileهای AutoMapper است:
تعریف سرویس مدیریت اتاقهای هتل
پس از راه اندازی برنامه و تعریف موجودیتها، DbContext و غیره، اکنون میتوانیم از آنها جهت ارائهی منطق مدیریتی برنامه استفاده کنیم:
که پیاده سازی ابتدایی آن به صورت زیر است:
- در اینجا DbContext برنامه و همچنین نگاشتگر AutoMapper، به سازندهی سرویس، تزریق شده و توسط آنها، ابتدا اطلاعات DTOها به موجودیتها تبدیل شدهاند (و یا برعکس) و سپس با استفاده از dbContext برنامه، کوئریهایی را بر روی بانک اطلاعاتی اجرا کردهایم.
- در این کدها استفاده از متد ProjectTo را هم مشاهده میکنید. استفاده از این متد، بسیار بهینهتر از کار با متد Map درون حافظهای است. از این جهت که بر روی SQL نهایی ارسالی به سمت سرور تاثیرگذار است و تعداد فیلدهای بازگشت داده شده را بر اساس DTO تعیین شده، کاهش میدهد. درغیراینصورت باید تمام ستونهای جدول را بازگشت داد و سپس با استفاده از متد Map درون حافظهای، کار نگاشت نهایی را انجام داد که آنچنان بهینه نیست.
در آخر نیاز است این سرویس را نیز به سیستم تزریق وابستگیهای برنامه معرفی کنیم. به همین جهت در فایل BlazorServer\BlazorServer.App\Startup.cs، تغییر زیر را اعمال خواهیم کرد:
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-13.zip
ساختار پوشهها و پروژههای قسمت Blazor Server
قسمت Blazor Server مدیریت هتل ما از 7 پروژه و پوشهی زیر تشکیل میشود:
- BlazorServer.App: پروژهی اصلی Blazor Server است که با اجرای دستور dotnet new blazorserver در پوشهی خالی آن آغاز میشود.
- BlazorServer.Common: پروژهای از نوع classlib، جهت قرارگیری کدهای مشترک بین پروژهها است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.DataAccess: پروژهای از نوع classlib، برای تعریف DbContext برنامه است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Entities: پروژهای از نوع classlib، جهت تعریف کلاسهای متناظر با جداول بانک اطلاعاتی برنامه است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Models: پروژهای از نوع classlib، برای تعریف کلاسهای data transfer objects برنامه (DTO's) است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Models.Mappings: پروژهای از نوع classlib، برای تعریف نگاشتهای بین DTO's و مجودیتهای برنامه و برعکس است که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
- BlazorServer.Services: پروژهای از نوع classlib، جهت تعریف کدهایی که منطق تجاری تعامل با بانک اطلاعاتی را از طریق BlazorServer.DataAccess میسر میکند که با اجرای دستور dotnet new classlib در این پوشه آغاز میشود.
اصلاح پروژهی BlazorServer.App جهت استفاده از LibMan
قالب پیشفرض BlazorServer.App، به همراه پوشهی wwwroot\css است که در آن بوت استرپ و open-iconic به همراه فایل site.css قرار دارند. چون این پروژه به همراه هیچ نوع روشی برای مدیریت نگهداری بستههای سمت کلاینت خود نیست، دو پوشهی بوت استرپ و open-iconic آنرا حذف کرده و از روش مطرح شدهی در مطلب «Blazor 5x - قسمت یازدهم - مبانی Blazor - بخش 8 - کار با جاوا اسکریپت» استفاده خواهیم کرد:
dotnet tool update -g Microsoft.Web.LibraryManager.Cli libman init libman install bootstrap --provider unpkg --destination wwwroot/lib/bootstrap libman install open-iconic --provider unpkg --destination wwwroot/lib/open-iconic
بعد از نصب بستههای ذکر شده، ابتدا سطر زیر را از ابتدای فایل پیشفرض wwwroot\css\site.css حذف میکنیم:
@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
<head> <meta charset="utf-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>BlazorServer.App</title> <base href="~/" /> <link href="lib/open-iconic/font/css/open-iconic-bootstrap.min.css" rel="stylesheet" /> <link href="lib/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet" /> <link href="css/site.css" rel="stylesheet" /> <link href="BlazorServer.App.styles.css" rel="stylesheet" /> </head>
برای bundling & minification این فایلها میتوان از «Bundler Minifier» استفاده کرد.
پروژهی موجودیتهای مدیریت هتل
فایل BlazorServer.Entities.csproj وابستگی خاصی را نداشته و به صورت زیر تعریف شدهاست:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> </Project>
using System; using System.ComponentModel.DataAnnotations; namespace BlazorServer.Entities { public class HotelRoom { [Key] public int Id { get; set; } [Required] public string Name { get; set; } [Required] public int Occupancy { get; set; } [Required] public decimal RegularRate { get; set; } public string Details { get; set; } public string SqFt { get; set; } public string CreatedBy { get; set; } public DateTime CreatedDate { get; set; } = DateTime.Now; public string UpdatedBy { get; set; } public DateTime UpdatedDate { get; set; } } }
پروژهی تعریف DbContext برنامهی مدیریت هتل
فایل BlazorServer.DataAccess.csproj به این صورت تعریف شدهاست:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\BlazorServer.Entities\BlazorServer.Entities.csproj" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="5.0.3"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> </Project>
- همچنین دو وابستگی مورد نیاز جهت کار با EntityFrameworkCore و اجرای مهاجرتها را نیز به آن افزودهایم.
پس از تامین این وابستگیها، اکنون میتوان DbContext ابتدایی برنامه را به صورت زیر تعریف کرد که کار آن، در معرض دید قرار دادن HotelRoom به صورت یک DbSet است:
using BlazorServer.Entities; using Microsoft.EntityFrameworkCore; namespace BlazorServer.DataAccess { public class ApplicationDbContext : DbContext { public DbSet<HotelRoom> HotelRooms { get; set; } public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { } } }
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\BlazorServer.DataAccess\BlazorServer.DataAccess.csproj" /> </ItemGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="5.0.3" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="5.0.3"> <PrivateAssets>all</PrivateAssets> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> </PackageReference> </ItemGroup> </Project>
- سپس چون میخواهیم از تامین کنندهی بانک اطلاعاتی SQL Server نیز استفاده کنیم، وابستگیهای آنرا نیز افزودهایم.
با این تنظیمات، به فایل BlazorServer\BlazorServer.App\Startup.cs مراجعه کرده و کار افزودن AddDbContext و UseSqlServer را انجام میدهیم تا DbContext برنامه از طریق تزریق وابستگیها قابل دسترسی شود و همچنین رشتهی اتصالی مشخص شده نیز به تامین کنندهی SQL Server ارسال شود:
namespace BlazorServer.App { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { var connectionString = Configuration.GetConnectionString("DefaultConnection"); services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(connectionString)); // ...
{ "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=HotelManagement;Trusted_Connection=True;MultipleActiveResultSets=true" } }
اجرای مهاجرتها و تشکیل ساختار بانک اطلاعاتی
پس از این تنظیمات، اکنون میتوانیم به پوشهی BlazorServer\BlazorServer.DataAccess مراجعه کرده و از طریق خط فرمان، دستورات زیر را صادر کنیم:
dotnet tool update --global dotnet-ef --version 5.0.3 dotnet build dotnet ef migrations --startup-project ../BlazorServer.App/ add Init --context ApplicationDbContext dotnet ef --startup-project ../BlazorServer.App/ database update --context ApplicationDbContext
- همیشه بهتر است پیش از اجرای عملیات Migration، یکبار dotnet build را اجرا کرد؛ تا اگر خطایی وجود دارد، بتوان جزئیات دقیق آنرا مشاهده کرد. چون عموما این جزئیات در حین اجرای دستورات بعدی، با پیام مختصر «عملیات شکست خورد»، نمایش داده نمیشوند.
- دستور سوم، کار تشکیل پوشهی BlazorServer\BlazorServer.DataAccess\Migrations و تولید خودکار دستورات تشکیل بانک اطلاعاتی را بر اساس ساختار DbContext برنامه انجام میدهد.
- دستور چهارم، بر اساس اطلاعات موجود در پوشهی BlazorServer\BlazorServer.DataAccess\Migrations، بانک اطلاعاتی واقعی را تولید میکند.
در این دستورات ذکر پروژهی آغازین برنامه جهت یافتن وابستگیهای پروژه ضروری است.
تکمیل پروژهی DTOهای برنامه
همواره توصیه شدهاست که موجودیتهای برنامه را مستقیما در معرض دید UI قرار ندهید. حداقل مشکلی را که در اینجا ممکن است مشاهده کنید، حملات از نوع mass assignment هستند. برای مثال قرار است از کاربر، کلمهی عبور جدید آنرا دریافت کنید، ولی چون اطلاعات دریافتی، به اصل موجودیت متناظر با بانک اطلاعاتی نگاشت میشود، کاربر میتواند فیلد IsAdmin را هم خودش مقدار دهی کند! و چون سیستم Binding بسیار پیشرفته عمل میکند، این ورودی را معتبر یافته و در اینجا علاوه بر به روز رسانی کلمهی عبور، خواص دیگری را هم که نباید به روز رسانی شوند، به روز رسانی میکند و یا در بسیاری از موارد نیاز است data annotations خاصی را برای فیلدها تعریف کرد که ربطی به موجودیت اصلی ندارند و یا نیاز است فیلدهایی را در UI قرار داد که باز هم تناظر یک به یکی با موجودیت اصلی ندارند (گاهی کمتر و گاهی بیشتر هستند و باید بر روی آنها محاسباتی صورت گیرد تا قابلیت ذخیره سازی در بانک اطلاعاتی را پیدا کنند). به همین جهت کار مدل سازی UI و یا بازگشت اطلاعات نهایی از سرویسها را توسط DTOها که یک سری کلاس سادهی C# 9.0 از نوع record هستند، انجام میدهیم:
using System; using System.ComponentModel.DataAnnotations; namespace BlazorServer.Models { public record HotelRoomDTO { public int Id { get; init; } [Required(ErrorMessage = "Please enter the room's name")] public string Name { get; init; } [Required(ErrorMessage = "Please enter the occupancy")] public int Occupancy { get; init; } [Range(1, 3000, ErrorMessage = "Regular rate must be between 1 and 3000")] public decimal RegularRate { get; init; } public string Details { get; init; } public string SqFt { get; init; } } }
در اینجا فیلدهای UI برنامه را که در قسمت بعد تکمیل خواهیم کرد، مشاهده میکنید؛ به همراه یک سری data annotation برای تعریف اجباری و یا بازهی مورد قبول، به همراه پیامهای خطای مرتبط.
نگاشت DTOهای برنامه به موجودیتها و بر عکس
یا میتوان خواص DTO تعریف شده را یکی یکی به موجودیتی متناظر با آن انتساب داد و یا میتوان از AutoMapper برای اینکار استفاده کرد. به همین جهت به BlazorServer.Models.Mappings.csproj مراجعه کرده و تغییرات زیر را اعمال میکنیم:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net5.0</TargetFramework> </PropertyGroup> <ItemGroup> <ProjectReference Include="..\BlazorServer.Entities\BlazorServer.Entities.csproj" /> <ProjectReference Include="..\BlazorServer.Models\BlazorServer.Models.csproj" /> </ItemGroup> <ItemGroup> <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="8.1.1" /> </ItemGroup> </Project>
- همچنین بستهی مخصوص AutoMapper را که به همراه امکانات تزریق وابستگیهای آن نیز هست، در اینجا افزودهایم.
پس از افزودن این ارجاعات، نگاشت دو طرفهی بین مدل و موجودیت تعریف شده را به صورت زیر تعریف میکنیم:
using AutoMapper; using BlazorServer.Entities; namespace BlazorServer.Models.Mappings { public class MappingProfile : Profile { public MappingProfile() { CreateMap<HotelRoomDTO, HotelRoom>().ReverseMap(); // two-way mapping } } }
namespace BlazorServer.App { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies()); // ...
تعریف سرویس مدیریت اتاقهای هتل
پس از راه اندازی برنامه و تعریف موجودیتها، DbContext و غیره، اکنون میتوانیم از آنها جهت ارائهی منطق مدیریتی برنامه استفاده کنیم:
using System.Collections.Generic; using System.Threading.Tasks; using BlazorServer.Models; namespace BlazorServer.Services { public interface IHotelRoomService { Task<HotelRoomDTO> CreateHotelRoomAsync(HotelRoomDTO hotelRoomDTO); Task<int> DeleteHotelRoomAsync(int roomId); IAsyncEnumerable<HotelRoomDTO> GetAllHotelRoomsAsync(); Task<HotelRoomDTO> GetHotelRoomAsync(int roomId); Task<HotelRoomDTO> IsRoomUniqueAsync(string name); Task<HotelRoomDTO> UpdateHotelRoomAsync(int roomId, HotelRoomDTO hotelRoomDTO); } }
که پیاده سازی ابتدایی آن به صورت زیر است:
using System; using System.Collections.Generic; using System.Threading.Tasks; using AutoMapper; using AutoMapper.QueryableExtensions; using BlazorServer.DataAccess; using BlazorServer.Entities; using BlazorServer.Models; using Microsoft.EntityFrameworkCore; namespace BlazorServer.Services { public class HotelRoomService : IHotelRoomService { private readonly ApplicationDbContext _dbContext; private readonly IMapper _mapper; private readonly IConfigurationProvider _mapperConfiguration; public HotelRoomService(ApplicationDbContext dbContext, IMapper mapper) { _dbContext = dbContext ?? throw new ArgumentNullException(nameof(dbContext)); _mapper = mapper ?? throw new ArgumentNullException(nameof(mapper)); _mapperConfiguration = mapper.ConfigurationProvider; } public async Task<HotelRoomDTO> CreateHotelRoomAsync(HotelRoomDTO hotelRoomDTO) { var hotelRoom = _mapper.Map<HotelRoom>(hotelRoomDTO); hotelRoom.CreatedDate = DateTime.Now; hotelRoom.CreatedBy = ""; var addedHotelRoom = await _dbContext.HotelRooms.AddAsync(hotelRoom); await _dbContext.SaveChangesAsync(); return _mapper.Map<HotelRoomDTO>(addedHotelRoom.Entity); } public async Task<int> DeleteHotelRoomAsync(int roomId) { var roomDetails = await _dbContext.HotelRooms.FindAsync(roomId); if (roomDetails == null) { return 0; } _dbContext.HotelRooms.Remove(roomDetails); return await _dbContext.SaveChangesAsync(); } public IAsyncEnumerable<HotelRoomDTO> GetAllHotelRoomsAsync() { return _dbContext.HotelRooms .ProjectTo<HotelRoomDTO>(_mapperConfiguration) .AsAsyncEnumerable(); } public Task<HotelRoomDTO> GetHotelRoomAsync(int roomId) { return _dbContext.HotelRooms .ProjectTo<HotelRoomDTO>(_mapperConfiguration) .FirstOrDefaultAsync(x => x.Id == roomId); } public Task<HotelRoomDTO> IsRoomUniqueAsync(string name) { return _dbContext.HotelRooms .ProjectTo<HotelRoomDTO>(_mapperConfiguration) .FirstOrDefaultAsync(x => x.Name == name); } public async Task<HotelRoomDTO> UpdateHotelRoomAsync(int roomId, HotelRoomDTO hotelRoomDTO) { if (roomId != hotelRoomDTO.Id) { return null; } var roomDetails = await _dbContext.HotelRooms.FindAsync(roomId); var room = _mapper.Map(hotelRoomDTO, roomDetails); room.UpdatedBy = ""; room.UpdatedDate = DateTime.Now; var updatedRoom = _dbContext.HotelRooms.Update(room); await _dbContext.SaveChangesAsync(); return _mapper.Map<HotelRoomDTO>(updatedRoom.Entity); } } }
- در این کدها استفاده از متد ProjectTo را هم مشاهده میکنید. استفاده از این متد، بسیار بهینهتر از کار با متد Map درون حافظهای است. از این جهت که بر روی SQL نهایی ارسالی به سمت سرور تاثیرگذار است و تعداد فیلدهای بازگشت داده شده را بر اساس DTO تعیین شده، کاهش میدهد. درغیراینصورت باید تمام ستونهای جدول را بازگشت داد و سپس با استفاده از متد Map درون حافظهای، کار نگاشت نهایی را انجام داد که آنچنان بهینه نیست.
در آخر نیاز است این سرویس را نیز به سیستم تزریق وابستگیهای برنامه معرفی کنیم. به همین جهت در فایل BlazorServer\BlazorServer.App\Startup.cs، تغییر زیر را اعمال خواهیم کرد:
namespace BlazorServer.App { public class Startup { // ... public void ConfigureServices(IServiceCollection services) { services.AddScoped<IHotelRoomService, HotelRoomService>(); // ...
کدهای کامل این مطلب را از اینجا میتوانید دریافت کنید: Blazor-5x-Part-13.zip
نظرات اشتراکها
عرضه Visual Studio 2013 Update 2 RTM
اینم از نسخه واحد برای Visual Studio 2013
Visual Studio 2013 with Update 2
Size 5.75 GB
CRC: F420C597
SHA-1: 46BFC982040172A8297F554985A09E617A41F8B7
اشتراکها
مقایسهای بین Xamarin.Forms و MAUI
Xamarin.Forms vs MAUI
| Xamarin.Forms | MAUI |
---|---|---|
Platforms | | |
Android | API 19+ | API 21+ |
iOS | 9-14 | 10+ |
Linux | Community | Community |
macOS | Community | Microsoft |
Tizen | Samsung | Samsung |
Windows | UWP Microsoft WPF Community | Microsoft |
Features | | |
Renderers | Tightly coupled to BindableObject | Loosely coupled, no Core dependencies |
App Models | MVVM, RxUI | MVVM, RxUI, MVU, Blazor |
Single Project | No | Yes |
Multi-targeting | No | Yes |
Multi-window | No | Yes |
Misc | | |
.NET | Xamarin.iOS, Xamarin.Android, Mono, .NET Framework, ... | .NET 6+ |
Acquisition | NuGet & Visual Studio Installer | dotnet |
Project System | Franken-proj | SDK Style |
dotnet CLI | No | Yes |
Tools | | |
Visual Studio 2019 | Yes | Yes |
Visual Studio 2019 for Mac | Yes | Yes |
Visual Studio Code | No | Yes |
از Microsoft StreamInsight استفاده میکنند.