توسعه کنترلر و مدل در F# MVC4
آموزش #F
ترجمه و تالیف: بهروز راد
وضعیت: در دست چاپ
Web API چیست؟
Web API، نوع قالب جدیدی برای پروژههای مبتنی بر وب در NET. است که بر مبنای اصول و الگوهای موجود در ASP.NET MVC ساخته شده است و همراه با ASP.NET MVC 4 وجود دارد. Web API توسعه گران را قادر میسازد تا با استفاده از یک الگوی ساده که در Controllerها پیاده سازی میشود، وب سرویسهای مبتنی بر پروتوکل HTTP را با کدها و تنظیمات کم ایجاد کنند. این سبک جدید برای ایجاد وب سرویس ها، میتواند در انواع پروژههای NET. مانند ASP.NET MVC، ASP.NET Web Forms، Windows Application و ... استفاده شود.
یک سوال کاملاً منطقی در اینجا به وجود میآید. چرا نیاز به بستری جدید برای ایجاد وب سرویس داریم؟ آیا در حال حاضر مایکروسافت بستری محبوب و فراگیر برای توسعهی وب سرویس هایی که بتوانند با پروتوکل SOAP تعامل داشته باشند در اختیار ندارد؟ مگر وب سرویسهای ASMX از زمان معرفی ASP.NET وجود نداشته اند؟ آیا تکنولوژی WCF مایکروسافت، بیشترین انعطاف پذیری و قدرت را برای تولید وب سرویسها در اختیار قرار نمیدهد؟ وب سرویسها جایگاه خود را یافته اند و توسعه گران با تکنولوژیهای موجود به خوبی آنها را پیاده سازی و درک میکنند. چرا Web API؟
چرا Web Api؟
برای پاسخ به این سوال، باید برخی مشکلات را بررسی کنیم و ببینیم ابزارهای موجود چه راه حلی برای آنها در نظر گرفته اند. اگر با گزینه هایی که در ادامه میآیند موافق هستید، خواندن این مطلب را ادامه دهید، و اگر اعتقادی به آنها ندارید، پس نیازهای شما به خوبی با بسترهای موجود پاسخ داده میشوند.
- من معتقد هستم که راه بهتری برای ایجاد وب سرویسها وجود دارد.
- من معتقد هستم که روشهای سادهتری برای ایجاد وب سرویسها وجود دارد و WCF بیش از حد پیچیده است.
- من معتقد هستم که تکنولوژیهای پایهی وب مانند اَفعال GET، POST، PUT و DELETE برای انجام اَعمال مختلف توسط وب سرویسها کافی هستند.
تفاوت Web API و WCF
وب سرویسهای ASMX تا چندین سال، انتخاب اول برای ایجاد وب سرویسهای مبتنی بر پروتوکل SOAP با استفاده از پروتوکل HTTP بودند. وب سرویسهای ASMX، از وب سرویسهای ساده که نیاز به قابلیت تعامل پایین داشتند و در نتیجه به پروتوکل SOAP نیز وابسته نبودند پشتیبانی نمیکردند. WCF جای وب سرویسهای ASMX را گرفت و خود را به عنوان آخرین و بهترین روش برای ایجاد وب سرویسها در بستر NET. معرفی کرد. نمونه ای از یک سرویس WCF بر مبنای پروتوکل HTTP در NET. به صورت ذیل است.
[ServiceContract] public interface IService1 { [OperationContract] string GetData(int value); [OperationContract] CompositeType GetDataUsingDataContract(CompositeType composite); } ... public class Service1 : IService1 { public string GetData(int value) { return string.Format("You entered: {0}", value); } public CompositeType GetDataUsingDataContract(CompositeType composite) { if (composite == null) { throw new ArgumentNullException("composite"); } if (composite.BoolValue) { composite.StringValue += "Suffix"; } return composite; } }
نتیجه گیری
Web API، یک روش جدید و آسان برای ایجاد وب سرویس ها، بر مبنای مفاهیم آشنای ASP.NET MVC و پایهی وب است. از این روش میتوان در انواع پروژههای NET. استفاده کرد.
دورههای دات نت در Microsoft Learn
اجرای دات نت در مروگر توسط Ooui
Earlier this year, Microsoft announced their support for Blazor, and now Frank A. Krueger has developed the Ooui library which allows C# or F# to be used to write applications that run in the browser. Ooui can target WASM, enabling Xamarin.Forms app to be deployed in web assembly and the result runs entirely in-browser without the need for an application server.
کد نویسی تعاملی #C و #F با REPLs
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "Data Source=(localdb)\mssqllocaldb;Initial Catalog=DbName;Encrypt=false;" Microsoft.EntityFrameworkCore.SqlServer -o Output -f
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="7.0.0" /> <PackageReference Include="Pomelo.EntityFrameworkCore.MySql.Design" Version="1.1.2" /> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "server=localhost;port=3306;user=root;password=MyPass;database=MyDbName;TreatTinyAsBoolean=true;AllowZeroDateTime=true;ConvertZeroDateTime=true;" Pomelo.EntityFrameworkCore.MySql -o Output -f
ج) Postgres
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="7.0.4"/> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "User ID=Vahid;Password=MyPass;Host=localhost;Port=5432;Database=MyDbName;Pooling=true;" Npgsql.EntityFrameworkCore.PostgreSQL -o Output -f
د) SQLite
وابستگیهای مورد نیاز در یک پروژهی classlib فرضی:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net7.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="7.0.10" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="7.0.10"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> </ItemGroup> </Project>
dotnet tool update --global dotnet-ef --version 7.0.10
dotnet ef dbcontext scaffold "Data Source=C:\\Path\\db.sqlite" Microsoft.EntityFrameworkCore.Sqlite -o Output
در این مطلب نحوهی استفاده از ReportGenerator را برای ایجاد گزارش مربوط به Code coverage، ارائه میدهیم. ReportGenerator دیتاهای تولید شدهی توسط coverlet, OpenCover, dotCover, Visual Studio, NCover, Cobertura, JaCoCo, Clover و ... را به یک گزارش قابل درک در فرمتهای Html, Coberura و CSV تبدیل میکند. در این مطلب چند موردتست نویسی با xUnit در Asp.Net Core را نوشتهام و اکنون میخواهیم برای تستهای نوشته شده، Code coverage آنها را بدست آوریم.
در کد زیر، سه تست برای متد AverageUsersAge نوشته شده است:
[Fact] public async Task AverageUsersAge_When_Repository_Return_Null_Then_Zero_Should_Be_Returned() { _userRepository.Setup(a => a.GetAllUsers()) .ReturnsAsync((List<User>)null); var result = await _userService.AverageUsersAge(); result.Should().Be(0); } [Fact] public async Task AverageUsersAge_When_Repository_Return_Empty_Then_Zero_Should_Be_Returned() { _userRepository.Setup(a => a.GetAllUsers()) .ReturnsAsync(new List<User>()); var result = await _userService.AverageUsersAge(); result.Should().Be(0); } [Fact] public async Task AverageUsersAge_When_Repository_Return_List_Of_Users_Then_Average_Of_Age_Should_Be_Retunred() { _userRepository.Setup(a => a.GetAllUsers()) .ReturnsAsync(UserMockData.People); var result = await _userService.AverageUsersAge(); var expected = (UserMockData.AdultUser.Age + UserMockData.ChildUser.Age + UserMockData.InfantUser.Age) / 3; result.Should().Be(expected); }
dotnet tool install -g dotnet-reportgenerator-globaltool
dotnet test --collect:"XPlat Code Coverage"
reportgenerator -reports:"C:\Users\Farhad\source\repos\xUnitExample\xUnitExample.Tests\TestResults\*\coverage.cobertura.xml" -targetdir:"coveragereport" -reporttypes:Html
2021-11-18T14:05:02: Arguments 2021-11-18T14:05:02: -reports:C:\Users\Farhad\source\repos\xUnitExample\xUnitExample.Tests\TestResults\*\coverage.cobertura.xml 2021-11-18T14:05:02: -targetdir:coveragereport 2021-11-18T14:05:02: -reporttypes:Html 2021-11-18T14:05:02: Writing report file 'coveragereport\index.html' 2021-11-18T14:05:02: Report generation took 0.3 seconds
در عکس ارسال شده، تستهای نوشته شده برای تمامی لایهها به صورت جدا ایجاد شدهاست.
تست مربوط به UserService:
در عکس بالا قسمتهایی که تست شدهاند، با رنگ سبز مشخص میشود و اگر قسمتی از کد تست نشده باشد، با رنگ قرمز مشخص میشود.
برای مثال در عکس زیر مشخص شدهاست که برای فایل ApplicationConfiguration هیچ تستی نوشته نشدهاست.
اگر از CICD استفاده میکنید، میتوانید در قسمت CI پروژه، هربار دستورات بالا را اجرا کنید و Code coverage مربوط به هر Build را به صورت جداگانه در کنار فایلهای آپلود شده داشته باشد.
نکته: برای اجرای دستورات بالا و ساخت گزارش باید ورژن SDK نصب شده بر روی سیستم شما برابر 2.2.401 یا بیشتر باشد و ورژن Microsoft.NET.Test.Sdk برابر 16.5.0 یا بیشتر باشد.