به مناسبت ارائهی نسخه 7.4 از Microsoft.AspNetCore.OData که دیروز صورت پذیرفت، تصمیم گرفتم آموزش استفاده از OData را در پروژههای ASP.NET Core 3.1 به بالا که دارای endpoint routing هستند (روش توصیه شده)، تهیه کنم تا در آن، پروژه کمترین تغییر ممکن را برای اضافه شدن OData داشته باشد و ببینیم که استفاده از آن در نسخههای جدید، به چه میزان آسان شده است.
ابتدا با dotnet --version و یا dotnet --info و یا هر روش دیگری، از نصب بودن dot net core 3.1 sdk مطمئن میشویم. سپس دستور
dotnet new webapi -o SampleApi
را میزنیم.
در ویژوال استودیو، این دستور معادل ساخت پروژهای جدید از نوع ASP.NET Core Web Application است که در دیالوگ بعدی، از بین گزینههای Empty، Api و Web Application و ... ما گزینهی Api را انتخاب میکنیم.
این یک پروژهی Web Api و با استفاده از endpoint routing است. در این پروژه یک WeatherForecast وجود دارد که نقش مدل را ایفا میکند و یک WeatherForecastController که در Get خود، تعدادی از WeatherForecastها را ساخته و باز میگرداند. این پروژه فاقد دیتابیس است.
حال چه کنیم که این پروژه OData enabled شود؟
1- دو Package زیر را نصب میکنیم:
<PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="3.1.3" />
<PackageReference Include="Microsoft.AspNetCore.OData" Version="7.4.0" />
2- در Startup.cs ابتدا
using Microsoft.AspNet.OData.Extensions;
را در بالای فایل اضافه کرده، و کدهای ConfigureServices را به شکل زیر تغییر میدهیم:
services.AddControllers().AddNewtonsoftJson(); // Add .AddNewtonsoftJson() to services.AddControllers();
services.AddOData(); // add this new line
همچنین کد app.UseEndpoints را به صورت زیر تغییر میدهیم:
app.UseEndpoints(endpoints => // Existing code
{
endpoints.MapControllers(); // Existing code
endpoints.EnableDependencyInjection(); // Add this new line
endpoints.Select().Expand().Filter().OrderBy().Count().MaxTop(20); // Add this new line
});
سپس در WeatherForecastController و متد Get، کد را به شکل زیر تغییر دهید:
// replace:
[HttpGet] // Existing code
// with:
[HttpGet, EnableQuery] // new code
برنامه را اجرا کنید و آدرس زیر را بزنید:
وجود orderby=TemperatureC باعث میشود که WeatherForcastها، مرتب شده بر اساس درجه سانتیگرادی که دارند، برای کلاینت ارسال شوند.
همانطور که مشاهده کردید، به سادهترین شکل ممکن، OData به پروژه اضافه شد!
به صورت کلی OData امکان فیلتر کردن رکوردهای بازگشتی، Projection، مرتب سازی، Paging، گروه بندی، Aggregation و ... را دارد که در ادامه چند مثالی را با هم میبینیم.
فقط با توجه به اینکه مثال پیش فرض ASP.NET Core، یعنی WeatherForecast کمی گنگ و غیر متداول است، از آن میگذریم و با فرض لیستی از مشتریان با ساختار زیر پیش میرویم:
public class Customer
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public Gender Gender { get; set; }
public AddressInfo Address { get; set; }
}
public class AddressInfo
{
public int StreetNo { get; set; }
public string PostalCode { get; set; }
}
public enum Gender
{
Man, Woman, Other
}
برای بازگردادن مشتریان خانم، داریم: (آموزش فیلتر بر روی enum)
?$filter=Gender eq 'Woman'
برای مرتب سازی مشتریان خانم بر اساس شماره خیابان آدرس آنها: (آموزش مرتب سازی بر روی Nested Properties و ترکیب orderby و filter)
?$filter=Gender eq 'Woman'&$orderby=Address/StreetNo
برای بازگرداندن مشتریانی که در اسم آنها کلمهی ali وجود دارد:
?$filter=contains(FirstName,'Ali')
برای مشاهده لیست امکانات کوئری گیری و مثالهای بیشتر میتوانید به
این لینک مراجعه کنید.
امکان کوئری گیری به صورت مفصل وجود دارد و میتوانید مواردی چون Any / All را نیز در فیلترهای خود بگنجانید.
به علاوه اگر به جای یک آرایه یا لیست، یک IQueryable از EntityFrameworkCore گرفته و ... بازگردانید، OData کوئری ارسالی را روی IQueryable مربوطه اعمال و paging و orderby و ... تماما روی دیتابیس انجام و فقط دیتای لازم و مورد نیاز از دیتابیس خوانده شده و به کلاینت باز میگردد که این مهم در بهبود عملکرد برنامه بسیار موثر است.
نکته مهم این است که در این روش، امنیت برنامه به خطر نمیافتد؛ زیرا اگر شما بر اساس منطق برنامه، یک Where را سمت سرور اعمال کنید، Client فقط میتواند روی دیتایی که حق دارد ببیند Paging و OrderBy و... اعمال کند، نه اینکه دیتای بیشتری را از سرور دریافت کند.
همچنین در این روش، استفاده از Swagger، Routing و ... تماما به روشی است که همین الان آن را بلدید، در کنار رعایت best practiceهایی چون بازگرداندن Dto به جای Entity و ... نیز کاملا امکان پذیر است.