المقدمة
دورة تدريبية قصيرة حول كيفية إنشاء تطبيق ويب بسيط باستخدام تقنيات ASP.NET Core و Entity Framework و Microsoft SQL Server DBMS و Angular framework. سنختبر Web API من خلال تطبيق Postman .
تتكون الدورة من عدة أجزاء:
- أنشئ واجهات برمجة تطبيقات الويب باستخدام ASP.NET Web API و Entity Framework Core.
- تنفيذ واجهة المستخدم في Angular.
- إضافة المصادقة إلى التطبيق.
- قم بتوسيع نموذج التطبيق واستكشاف الميزات الإضافية لـ Entity Framework.
الجزء 1. إنشاء واجهات برمجة تطبيقات الويب باستخدام ASP.NET Web API و Entity Framework Core
كمثال ، سننظر في التطبيق الكلاسيكي الآن لقائمة المهام. لتطوير التطبيق ، سأستخدم Visual Studio 2019 (في Visual Studio 2017 ، العملية مماثلة).
إنشاء المشروع
إنشاء مشروع تطبيق ويب ASP.NET Core جديد في Visual Studio: قم
بتسمية التطبيق وتحديد المسار إلى الدليل بالمشروع:
وحدد قالب تطبيق API:
نموذج
دعونا ننشئ كتالوج النماذج ونضيف أول فئة TodoItem.cs إلى الكتالوج الجديد ، والتي ستصف كائناتها بعض مهام قائمة المهام في التطبيق:
public class TodoItem
{
public int Id { get; set; }
public string TaskDescription { get; set; }
public bool IsComplete { get; set; }
}
سنستخدم Sql Server باعتباره DBMS ، وسيتم الوصول إلى قاعدة البيانات من خلال Entity Framework Core ، وسنقوم أولاً بتثبيت إطار العمل من خلال مدير الحزم المدمج في NuGet:
أحد أساليب العمل مع Entity Framework هو نهج "Code-First". جوهر النهج هو أنه بناءً على نموذج التطبيق (في حالتنا ، يمثل النموذج فئة واحدة - TodoItem.cs) ، يتم تشكيل بنية قاعدة البيانات (الجداول ، المفاتيح الأساسية ، الروابط) ، كل هذا العمل يحدث "خلف الكواليس" وبشكل مباشر مع نحن لا نعمل مع SQL. الشرط الأساسي لفئة النموذج هو وجود حقل مفتاح أساسي ؛ بشكل افتراضي ، يبحث Entity Framework عن حقل عدد صحيح يوجد باسمه "معرف" للسلسلة الفرعية ويشكل مفتاحًا أساسيًا بناءً عليه. يمكنك تجاوز هذا السلوك باستخدام السمات المخصصة أو استخدام إمكانيات Fluent API.
المكون الرئيسي في العمل مع Entity Framework هو فئة سياق قاعدة البيانات ، والتي من خلالها يتم الوصول إلى البيانات الموجودة في الجداول بالفعل:
public class EFTodoDBContext : DbContext
{
public EFTodoDBContext(DbContextOptions<EFTodoDBContext> options) : base(options)
{ }
public DbSet<TodoItem> TodoItems{ get; set; }
}
تنشئ الفئة الأساسية DbContext سياق قاعدة البيانات وتوفر الوصول إلى وظائف Entity Framework.
سنستخدم SQL Server 2017 Express لتخزين بيانات التطبيق . يتم تخزين سلاسل الاتصال في ملف JSON يسمى appsettings.json:
{
"ConnectionStrings": {
"DefaultConnection": "Server=.\\SQLEXPRESS;Database=Todo;Trusted_Connection=true"
}
}
بعد ذلك ، تحتاج إلى تعديل فئة Startup.cs عن طريق إضافة الكود التالي إلى طريقة ConfigureServices ():
services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
تعمل طريقة AddDbContext () على تكوين الخدمات التي يوفرها Entity Framework Core لفئة سياق قاعدة بيانات EFTodoDBContext. الوسيطة إلى أسلوب AddDbContext () هي تعبير lambda يتلقى كائن خيارات يقوم بتكوين قاعدة البيانات لفئة السياق. في هذه الحالة ، يتم تكوين قاعدة البيانات باستخدام أسلوب UseSqlServer () وتحديد سلسلة اتصال.
دعنا نحدد العمليات الأساسية للعمل مع المهام في واجهة ITodoRepository:
public interface ITodoRepository
{
IEnumerable<TodoItem> Get();
TodoItem Get(int id);
void Create(TodoItem item);
void Update(TodoItem item);
TodoItem Delete(int id);
}
تسمح لنا هذه الواجهة بعدم التفكير في التنفيذ المحدد لمستودع البيانات ، ربما لم نقرر بالضبط اختيار إطار عمل DBMS أو ORM ، الآن لا يهم ، الفئة التي تصف الوصول إلى البيانات سوف ترث من هذه الواجهة.
دعنا ننفذ مستودعًا ، كما ذكرنا سابقًا ، سيرث من ITodoRepository ويستخدم EFTodoDBContext كمصدر بيانات:
public class EFTodoRepository : ITodoRepository
{
private EFTodoDBContext Context;
public IEnumerable<TodoItem> Get()
{
return Context.TodoItems;
}
public TodoItem Get(int Id)
{
return Context.TodoItems.Find(Id);
}
public EFTodoRepository(EFTodoDBContext context)
{
Context = context;
}
public void Create(TodoItem item)
{
Context.TodoItems.Add(item);
Context.SaveChanges();
}
public void Update(TodoItem updatedTodoItem)
{
TodoItem currentItem = Get(updatedTodoItem.Id);
currentItem.IsComplete = updatedTodoItem.IsComplete;
currentItem.TaskDescription = updatedTodoItem.TaskDescription;
Context.TodoItems.Update(currentItem);
Context.SaveChanges();
}
public TodoItem Delete(int Id)
{
TodoItem todoItem = Get(Id);
if (todoItem != null)
{
Context.TodoItems.Remove(todoItem);
Context.SaveChanges();
}
return todoItem;
}
}
مراقب
لن تعرف وحدة التحكم ، التي سيتم وصف تنفيذها أدناه ، أي شيء عن سياق بيانات EFTodoDBContext ، ولكنها ستستخدم فقط واجهة ITodoRepository في عملها ، مما يسمح بتغيير مصدر البيانات دون تغيير وحدة التحكم. هذا النهج آدم فريمان في كتابه "Entity Framework Core 2 لـ ASP.NET Core MVC للمحترفين" يسمى نمط "التخزين".
تنفذ وحدة التحكم معالجات لطرق طلب HTTP القياسية: GET و POST و PUT و DELETE ، والتي ستغير حالة مهامنا الموضحة في فئة TodoItem.cs.
أضف فئة TodoController.cs إلى دليل Controllers بالمحتوى التالي:
[Route("api/[controller]")]
public class TodoController : Controller
{
ITodoRepository TodoRepository;
public TodoController(ITodoRepository todoRepository)
{
TodoRepository = todoRepository;
}
[HttpGet(Name = "GetAllItems")]
public IEnumerable<TodoItem> Get()
{
return TodoRepository.Get();
}
[HttpGet("{id}", Name = "GetTodoItem")]
public IActionResult Get(int Id)
{
TodoItem todoItem = TodoRepository.Get(Id);
if (todoItem == null)
{
return NotFound();
}
return new ObjectResult(todoItem);
}
[HttpPost]
public IActionResult Create([FromBody] TodoItem todoItem)
{
if (todoItem == null)
{
return BadRequest();
}
TodoRepository.Create(todoItem);
return CreatedAtRoute("GetTodoItem", new { id = todoItem.Id }, todoItem);
}
[HttpPut("{id}")]
public IActionResult Update(int Id, [FromBody] TodoItem updatedTodoItem)
{
if (updatedTodoItem == null || updatedTodoItem.Id != Id)
{
return BadRequest();
}
var todoItem = TodoRepository.Get(Id);
if (todoItem == null)
{
return NotFound();
}
TodoRepository.Update(updatedTodoItem);
return RedirectToRoute("GetAllItems");
}
[HttpDelete("{id}")]
public IActionResult Delete(int Id)
{
var deletedTodoItem = TodoRepository.Delete(Id);
if (deletedTodoItem == null)
{
return BadRequest();
}
return new ObjectResult(deletedTodoItem);
}
}
قبل تعريف الفئة ، تم تحديد سمة تصف قالب المسار للوصول إلى وحدة التحكم: [المسار ("api / [وحدة التحكم]")]. يمكن الوصول إلى TodoController عبر المسار التالي: https: // <host ip>: <port> / api / todo. يحدد [Controller] اسم فئة وحدة التحكم بالأحرف الصغيرة ، مع حذف جزء "Controller".
قبل تحديد كل طريقة في TodoController ، يتم تحديد سمة خاصة للنموذج: [<HTTP method> ("المعلمة" ، الاسم = "الاسم المستعار للطريقة")]. تحدد السمة طلب HTTP الذي ستتم معالجته بهذه الطريقة ، والمعلمة التي تم تمريرها في URI للطلب والاسم المستعار للطريقة التي يمكن إعادة إرسال الطلب بها. إذا لم تحدد السمة ، فسيحاول إطار عمل MVC افتراضيًا العثور على الطريقة الأنسب في وحدة التحكم لمعالجة الطلب بناءً على اسم الطريقة والمعلمات المحددة في الطلب ، لذلك إذا لم تحدد سمة لطريقة Get () في وحدة تحكم TodoController ، ثم في طلب HTTP باستخدام طريقة GET: https: // <host ip>: <port> / api / todo ، ستحدد البنية التحتية طريقة Get () لوحدة التحكم لمعالجة الطلب.
في مُنشئها ، تتلقى وحدة التحكم مرجعًا إلى كائن من النوع ITodoRepository ، ولكن حتى الآن لا تعرف البنية الأساسية لـ MVC أي كائن يجب استبداله عند إنشاء وحدة التحكم. نحتاج إلى إنشاء خدمة تعمل على حل هذه التبعية بشكل فريد ، لذلك سنجري بعض التغييرات على فئة Startup.cs عن طريق إضافة الكود التالي إلى طريقة ConfigureServices ():
services.AddTransient<ITodoRepository, EFTodoRepository>();
تحدد طريقة AddTransient <ITodoRepository، EFTodoRepository> () خدمة تنشئ مثيلاً جديدًا لفئة EFTodoRepository عندما يكون مثيل من نوع ITodoRepository مطلوبًا ، على سبيل المثال في وحدة تحكم.
الكود الكامل لفئة Startup.cs:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddDbContext<EFTodoDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:DefaultConnection"]));
services.AddTransient<ITodoRepository, EFTodoRepository>();
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
الهجرات
لكي يقوم Entity Framework بإنشاء قاعدة البيانات والجداول من النموذج ، يجب عليك استخدام عملية ترحيل قاعدة البيانات. عمليات الترحيل هي مجموعة من الأوامر التي تعد قاعدة البيانات للعمل مع Entity Framework. يتم استخدامها لإنشاء قاعدة البيانات ومزامنتها. يمكن تنفيذ الأوامر في كل من وحدة تحكم مدير الحزم وفي Power Shell (Developer Power Shell). سوف نستخدم Package Manager Console ، للعمل مع Entity Framework ، نحتاج إلى تثبيت حزمة Microsoft.EntityFrameworkCore.Tools:
قم بتشغيل وحدة تحكم مدير الحزمة وتنفيذ الأمر الأولي للترحيل الإضافي :
سيظهر دليل جديد في المشروع - عمليات الترحيل ، حيث سيتم تخزين فئات الترحيل ، على أساس العناصر التي سيتم إنشاؤها في قاعدة البيانات بعد تنفيذ أمر تحديث قاعدة البيانات:
واجهة برمجة تطبيقات الويب جاهزة ، من خلال تشغيل التطبيق على IIS Express المحلي ، يمكننا اختبار وحدة التحكم.
اختبار WebAPI
لنقم بإنشاء مجموعة جديدة من الطلبات في Postman تسمى TodoWebAPI:
نظرًا لأن قاعدة البيانات الخاصة بنا فارغة ، فلنختبر إنشاء مهمة جديدة أولاً. في وحدة التحكم ، تكون طريقة Create () مسؤولة عن إنشاء المهام ، والتي ستعالج طلب HTTP المرسل بواسطة طريقة POST وسيحتوي على كائن TodoItem متسلسل بتنسيق JSON في نص الطلب. تخبر السمة [FromBody] قبل معلمة todoItem في طريقة Create () إطار عمل MVC بإلغاء تسلسل كائن TodoItem من نص الطلب وتمريره كمعامل إلى الطريقة. لنقم بإنشاء طلب في Postman سيرسل طلبًا إلى webAPI لإنشاء مهمة جديدة:
تقوم طريقة Create () بعد الإنشاء الناجح للمهمة بإعادة توجيه الطلب إلى طريقة Get () بالاسم المستعار "GetTodoItem" وتمرير معرف المهمة التي تم إنشاؤها حديثًا كمعامل ، ونتيجة لذلك سوف نتلقى كائن المهمة الذي تم إنشاؤه بتنسيق JSON استجابةً للطلب.
عن طريق إرسال طلب HTTP باستخدام طريقة PUT وتحديد كائن تم إنشاؤه بالفعل في معرف URI (https: // localhost: 44370 / api / todo / 1) ، وفي نص الطلب الذي يمرر كائنًا مع بعض التغييرات في تنسيق JSON ، سنقوم بتغيير هذا الكائن في قاعدة البيانات :
مع طلب HTTP باستخدام طريقة GET دون تحديد المعلمات ،
سنتلقى جميع الكائنات في قاعدة البيانات: طلب HTTP باستخدام طريقة DELETE وتحديد معرف الكائن في URI (https: // localhost: 44370 / api / todo / 2) ، سيحذف الكائن من قاعدة البيانات ويعيد JSON باستخدام مهمة عن بعد:
هذا كل شيء ، في الجزء التالي سنقوم بتنفيذ واجهة المستخدم باستخدام إطار Angular JavaScript.