Web API Development in .NET 8 in 2 Hours | ASP.NET CORE | RESTFUL API
00:00:00 Introduction
00:03:06 What is Web API & Why create Web API
00:10:21 How Web API Works in Theory
00:14:49 How Web API Works (Demo with Minimal APIs)
00:27:32 What is a Web API Framework
00:33:27 ASP.NET Core Middleware Pipeline
00:37:34 Web API Controller
00:42:25 Routing in Web API
00:51:17 Model Binding
01:01:06 Model Validation with DataAnnatation
01:08:07 Model Validation with ValidationAttribute
01:15:10 Web API Return Types
01:21:30 In Mememory Repository
01:25:01 Model Validation with Action Filter
01:35:30 Read Endpoint
01:36:55 Create Endpoint
01:46:45 Validating Create Endpoint with ActionFilter
01:51:23 Update Endpoint
01:59:48 Exception Hanlding with Exception Filter
02:05:48 Delete Endpoint
روش TPH
در این روش، ارث بری از طریق فقط یک جدول ایجاد میشود و زیر مجموعهها بر اساس مقدار یک فیلد از یکدیگر متمایز میشوند. پس اگر جدولی دارید که برای متمایز کردن رکوردهای آن از یک فیلد استفاده میکنید، روش TPH مناسب شما است. با روش TPH نیز میتوانید به همان مدلی که در روش TPT دارید برسید، تنها تفاوت این هست که در روش TPH، تمامی دادهها در یک جدول قرار دارند و یک فیلد برای متمایز کردن رکوردها استفاده میشود. همه چیز با مثال عملی واضحتر است. پس کار خود را با یک مثال ادامه میدهیم. جدول مثال ما در شکل زیر مشخص است.
به نظر میرسد که این جدول با جداول قسمت قبل شباهتی دارد. بله! فیلدهای جداول مثال قبل در این جدول آمده اند.
- فیلدهای FirstName و LastName از جدول Persons
- فیلد HireDate از جدول Instructors
- فیلد EnrollmentDate، Credits و Degree از جدول Students
- فیلد AdminDate از جدول Admins
- فیلدهای BusinessCredits و Discipline از جدول BusinessStudents
یک فیلد با نام PersonCategory نیز اضافه شده است که «مقداری عددی» میپذیرد و برای «متمایز کردن رکوردها» استفاده میشود:
- 1 ، نمایانگر Student
- 2 ، نمایانگر Instructor
- 3 ، نمایانگر Admin
- 4 ، نمایانگر BusinessStudent
از این جدول میخواهیم به مدل قسمت اول برسیم اما این بار با استفاده از روش TPH. در شکل زیر، جدول Persons به صورت مدل شده در برنامه نشان داده شده است.
حال باید خصیصههای موجودیت Person را به موجودیتهای مشتق شده منتقل کرد. بدین منظور، هر خصیصه از موجودیت Person را انتخاب، کلیدهای Ctrl+X را فشار دهید، سپس بر روی قسمت Properties موجودیت مشتق شدهی مورد نظر رفته و کلیدهای Ctrl+V را فشار دهید. نتیجه در شکل زیر نشان داده شده است.
اکنون زمان آن رسیده است تا جدول متناظر با هر یک از موجودیتهای مشتق شده را معرفی کنیم. تمامی موجودیتهای مشتق شده از جدول Persons استفاده میکنند. بر روی هر یک از آنها کلیک راست کرده و گزینهی Table Mapping را انتخاب کنید. پنجره ی Mapping Details نشان داده میشود. ابتدا بر روی عبارت Add a Table or View و سپس بر روی نشانگر رو به پایینی که کنار آن ظاهر میشود کلیک کنید (شکل زیر).
آیتم Persons را انتخاب کنید. اکنون باید فیلد تفکیک کنندهی رکوردها را مشخص کنیم. برای این حالت باید یک شرط ایجاد نمود. در همان پنجرهی Mapping Details، عبارتی با عنوان Add a Condition وجود دارد. بر روی آن کلیک و در لیستی که ظاهر میشود، آیتم PersonCategory را انتخاب کنید (شکل زیر).
سپس در ستون Value/Property، مقدار آن را "1" قرار دهید (شکل زیر).
تناظر میان موجودیت و جدول Persons و مقداردهی مناسب به فیلد متمایز کننده را برای تمامی موجودیتهای مشتق شده انجام دهید. دلیل این کار این است که EF بداند هر رکورد در چه زمانی باید به چه موجودیتی تبدیل شود. دقت کنید که پیشتر به مقدار فیلد متمایز کننده برای هر موجودیت اشاره کردیم. نکتهی مهم اینکه یک شرط نیز باید برای موجودیت Person ایجاد و مقدار فیلد متمایز کنندهی آن را "صفر" تعریف کنید.
مثال ما آماده است. آن را امتحان میکنیم.
using (PersonDbEntities context = new PersonDbEntities()) { var people = from p in context.Persons select p; foreach (Person person in people) { Console.WriteLine("{0}, {1}", person.LastName, person.FirstName); if (person is Student) Console.WriteLine(" Degree: {0}", ((Student)person).Degree); if (person is BusinessStudent) Console.WriteLine(" Discipline: {0}", ((BusinessStudent)person).Discipline); } Console.ReadLine(); }
مزایای روش TPH
- سرعت بالای عملیات CRUD، به دلیل وجود تمامی دادهها در یک جدول
- تعداد جداول در پایگاه داده، کم و مدیریت آنها آسانتر است
معایب روش TPH
- افزونگی داده ها. مقادیر برخی ستونها برای بعضی از رکوردها، حاوی مقدار NULL است و تعداد این ستونها به تعداد زیر مجموعهها ارتباط دارد
- عیب اول، باعث میشود تا در صورتی که دادهها به صورت دستی تغییر پیدا کنند، جامعیت دادهها از بین برود
- افزایش بی دلیل حجم داده ها
- اضافه و حذف کردن موجودیتها به مدل، عملی زمانبر و پیچیده است
قالب WinUI برای WPF
معرفی وب سایت Green Sock
زبان D
SortedSet قرار گرفته در فضای نام System.Collections.Generic دات نت 4، لیستی از اشیاء به صورت خودکار مرتب شده را ارائه میدهد. SortedSet نیز همانند HashSet از اعضای منحصربفردی تشکیل خواهد شد اما اینبار به شکلی مرتب شده. برای پیاده سازی آن از red-black tree data structure استفاده شده است که مهمترین مزیت آن امکان افزودن و یا حذف اشیاء به آن بدون کاهش قابل توجه کارآیی برنامه است.
مثال اول:
using System;
using System.Collections.Generic;
namespace SortedSetTest
{
class Program
{
static void sample1()
{
var setRange = new SortedSet<int> { 2, 5, 6, 2, 1, 4, 8 };
foreach (var i in setRange)
{
Console.WriteLine(i);
}
}
static void Main()
{
sample1();
}
}
}
مثال دوم:
using System;
using System.Collections.Generic;
namespace SortedSetTest
{
class Program
{
static void sample2()
{
var setRange = new SortedSet<int>();
var random = new Random();
for (int counter = 0; counter < 100; counter++)
{
var rnd = random.Next(-180, 181);
if (!setRange.Add(rnd))
{
Console.WriteLine("Couldn't add {0}", rnd);
}
}
Console.WriteLine("Result set:");
foreach (var item in setRange)
{
Console.WriteLine(item);
}
}
static void Main()
{
sample2();
}
}
}
مثال سوم:
اگر از سایر انواع سفارشی تعریف شده استفاده نمائید، باید روش مقایسهی آنها را نیز با پیاده سازی اینترفیس استاندارد IComparable ارائه دهید؛ در غیر اینصورت با خطای At least one object must implement IComparable متوقف خواهید شد.
using System;
using System.Collections;
using System.Collections.Generic;
namespace SortedSetTest
{
class FileInfo
{
public string Name { set; get; }
public long Size { set; get; }
}
class FileInfoComparer : IComparer<FileInfo>
{
public int Compare(FileInfo x, FileInfo y)
{
var caseiComp = new CaseInsensitiveComparer();
return caseiComp.Compare(x.Name, y.Name);
}
}
class Program
{
static void sample3()
{
var setRange = new SortedSet<FileInfo>(new FileInfoComparer())
{
new FileInfo
{
Name = "file1.txt",
Size = 100
},
new FileInfo
{
Name = "file2.txt",
Size = 10
},
new FileInfo
{
Name = "file3.txt",
Size = 300
}
};
foreach (var item in setRange)
{
Console.WriteLine(item.Name);
}
}
static void Main()
{
sample3();
Console.WriteLine("Press a key...");
Console.ReadKey();
}
}
}
در این مثال اشیایی از نوع کلاس FileInfo به لیست ویژهی ما اضافه شدهاند. برای اینکه امکان مقایسهی آنها فراهم باشد ، کلاس FileInfoComparer با پیاده سازی اینترفیس IComparer ، روش مقایسه دو شیء از این دست را ارائه میدهد.
کتاب C# Code Contracts Succinctly
Developed by Microsoft’s Research in Software Engineering, Code Contracts provide a way to convey code assumptions in your .NET applications. They can take the form of preconditions, postconditions, and state invariants. In C# Code Contracts Succinctly, author Dirk Strauss demonstrates how to use Code Contracts to validate logical correctness in code, how they can be integrated with abstract classes and interfaces, and even how they can be used to make writing documentation less painful.
class Car { } class CarProducer { public void DeliverTo(int carsCount, string town) { Car[] cars = new Car[carsCount]; ... } }
class Transporter { public string Name { get; private set; } public Transporter(string name) { this.Name = name; } public void Deliver(Car[] cars, string town) { Console.WriteLine("Delivering {0} car(s) to {1} by {2}", cars.Length, town, this.Name); } }
static class TransporterLocator { static IList<Transporter> transporters = new List<Transporter>(); public static void Register(Transporter transporter) { transporters.Add(transporter); } public static Transporter Locate(string name) { return transporters .Where(transporter => transporter.Name == name) .Single(); } }
class CarProducer { public void DeliverTo(int carsCount, string town) { Car[] cars = new Car[carsCount]; Transporter transporter = null; if (carsCount <= 12) transporter = TransporterLocator.Locate("truck"); else transporter = TransporterLocator.Locate("train"); transporter.Deliver(cars, town); } }
TransporterLocator.Register(new Transporter("truck")); TransporterLocator.Register(new Transporter("train")); CarProducer producer = new CarProducer(); producer.DeliverTo(7, "Tehran"); producer.DeliverTo(74, "Tehran");
TransporterLocator.Register(new Transporter("truck")); CarProducer producer = new CarProducer(); producer.DeliverTo(7, "Tehran"); producer.DeliverTo(74, "Tehran");
class CarProducer { private Transporter truck; private Transporter train; public CarProducer(Transporter truck, Transporter train) { if (truck == null) throw new ArgumentNullException("truck"); if (train == null) throw new ArgumentNullException("train"); this.truck = truck; this.train = train; } public void DeliverTo(int carsCount, string town) { Car[] cars = new Car[carsCount]; Transporter transporter = this.truck; if (carsCount > 12) transporter = this.train; transporter.Deliver(cars, town); } }
آیا وضعیتی وجود دارد که در آن Service Locator یک راه حل قابل قبول باشد؟
در برخی موارد بجای اینکه وابستگیها را به صورت صریح قید کنیم، بهتر است از این الگو استفاده کنیم.
در این سیستم، کلیه اتصالاتی که Mode آنها به OneTime تنظیم نشده است، به صورت اجباری دارای یک valueChangedHandlers متصل توسط سیستم PropertyDescriptor خواهند بود و در حافظه زنده نگه داشته میشوند؛ تا بتوان در صورت نیاز، توسط سیستم binding اطلاعات آنها را به روز کرد.
همین مساله سبب میشود تا اگر قرار نیست خاصیتی برای نمونه توسط مکانیزم INotifyPropertyChanged اطلاعات UI را به روز کند (یک خاصیت معمولی دات نتی است) و همچنین حالت اتصال آن به OneTime نیز تنظیم نشده، سبب مصرف حافظه بیش از حد برنامه شود.
اطلاعات بیشتر
A memory leak may occur when you use data binding in Windows Presentation Foundation
راه حل آن هم ساده است. برای اینکه valueChangedHandler ایی به خاصیت سادهای که قرار نیست بعدها UI را به روز کند، متصل نشود، حالت اتصال آنرا باید به OneTime تنظیم کرد.
سؤال: در یک برنامه بزرگ که هم اکنون مشغول به کار است، چطور میتوان این مسایل را ردیابی کرد؟
برای دستیابی به اطلاعات کش Binding در WPF، باید به Reflection متوسل شد. به این ترتیب در برنامه جاری، در کلاس PropertyDescriptor به دنبال یک کلاس خصوصی تو در توی دیگری به نام ReflectTypeDescriptionProvider خواهیم گشت (این اطلاعات از طریق مراجعه به سورس دات نت و یا حتی برنامههای ILSpy و Reflector قابل استخراج است) و سپس در این کلاس خصوصی داخلی، فیلد خصوصی propertyCache آنرا که از نوع HashTable است استخراج میکنیم:
var reflectTypeDescriptionProvider = typeof(PropertyDescriptor).Module.GetType("System.ComponentModel.ReflectTypeDescriptionProvider"); var propertyCacheField = reflectTypeDescriptionProvider.GetField("_propertyCache", BindingFlags.Static | BindingFlags.NonPublic);
اکنون به لیست داخلی Binding نگهداری شونده توسط WPF دسترسی پیدا کردهایم. در این لیست به دنبال مواردی خواهیم گشت که فیلد valueChangedHandlers به آنها متصل شده است و در حال گوش فرا دادن به سیستم binding هستند (سورس کامل و طولانی این مبحث را در پروژه پیوست شده میتوانید ملاحظه کنید).
یک مثال: تعریف یک کلاس ساده، اتصال آن و سپس بررسی اطلاعات درونی سیستم Binding
فرض کنید یک کلاس مدل ساده به نحو ذیل تعریف شده است:
namespace WpfOneTime.Models { public class User { public string Name { set; get; } } }
using WpfOneTime.Models; using System.Collections.Generic; namespace WpfOneTime.ViewModels { public class MainWindowViewModel { public IList<User> Users { set; get; } public MainWindowViewModel() { Users = new List<User>(); for (int i = 0; i < 1000; i++) { Users.Add(new User { Name = "name " + i }); } } } }
<Window x:Class="WpfOneTime.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ViewModels="clr-namespace:WpfOneTime.ViewModels" Title="MainWindow" Height="350" Width="525"> <Window.Resources> <ViewModels:MainWindowViewModel x:Key="vmMainWindowViewModel" /> </Window.Resources> <Grid DataContext="{Binding Source={StaticResource vmMainWindowViewModel}}"> <ListBox ItemsSource="{Binding Users}"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Name}" /> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
خوب؛ اکنون اگر اطلاعات HashTable داخلی سیستم Binding را در مورد View فوق بررسی کنیم به شکل زیر خواهیم رسید:
بله. تعداد زیادی خاصیت Name زنده و موجود در حافظه باقی هستند که تحت ردیابی سیستم Binding میباشند.
در ادامه، نکتهی ابتدای بحث را جهت تعیین حالت Binding به OneTime، به View فوق اعمال میکنیم (یک سطر ذیل باید تغییر کند):
<TextBlock Text="{Binding Name, Mode=OneTime}" />
به این ترتیب میتوان در لیستهای طولانی، به مصرف حافظه کمتری در برنامه WPF خود رسید.
بدیهی است این نکته را تنها در مواردی میتوان اعمال کرد که نیاز به بهروز رسانیهای ثانویه اطلاعات UI در کدهای برنامه وجود ندارند.
چطور از این نکته برای پروفایل یک برنامه موجود استفاده کنیم؟
کدهای برنامه را از انتهای بحث دریافت کنید. سپس دو فایل ReflectPropertyDescriptorWindow.xaml و ReflectPropertyDescriptorWindow.xaml.cs آنرا به پروژه خود اضافه نمائید و در سازنده پنجره اصلی برنامه، کد ذیل را فراخوانی نمائید:
new ReflectPropertyDescriptorWindow().Show();
دریافت کدهای کامل پروژه این مطلب
WpfOneTime.zip