بررسی روش Database-First در Entity Framework Core

در این آموزش از وبسایت پرووید قصد داریم به شما آموزش دهیم که چگونه کلاس Context و Entity Class ها را از یک Database ای که از قبل موجود هست با استفاده از Entity Framework Core بسازید. ساختن Entity Class ها و کلاس Context از طریق دیتابیس ای که موجود هست را روش Database-First می‌نامند. دقت کنید که علی‌ رغم قابلیت هایی که در Entity Framework 6 وجود داشت، در Entity Framework Core یک Visual Designer برای ساخت Model و یا یک ویزاد برای ساخت Entity Class ها و کلاس Context وجود ندارد.

در Entity Framework Core باید اصطلاحاً مهندسی معکوس یا Reverse Engineering انجام داد. برای انجام این روال مهندسی معکوس از دستور Scaffold-DbContext استفاده می‌ کنیم. این دستور Entity Class ها و کلاس Context را بر اساس Schema بانک اطلاعاتی ایجاد می کند. طبیعتا می‌ دانید که کلاس Context با ارث بری کردن از کلاس DbContext ساخته می شود. در این آموزش از وب سایت پرووید ما قصد داریم با استفاده از یک بانک اطلاعاتی به نام SchoolDB که در در MS SQL Server ساخته شده است کلاس Context و Entity Class های مربوط به جداول آن را بسازیم.

تصویر زیر Schema این دیتابیس را نشان داده شده است.


معرفی دستور Scaffold-DbContext

با استفاده از دستور Scaffold-DbContext شما می توانید Model خود را بر اساس یک دیتابیس موجود در Entity Framework Core بسازید. پارامترهای مختلف این دستور که در Package Manager Console می‌توانند لحاظ شوند را در قسمت زیر مشاهده می کنید.

Scaffold-DbContext [-Connection] [-Provider] [-OutputDir] [-Context] [-Schemas>] [-Tables>]

[-DataAnnotations] [-Force] [-Project] [-StartupProject] []

برای انجام کار ما با استفاده از این دستور به تمامی پارامترهای آن نیازی نداریم. بنابراین به طور ساده از طریق منوی Tools و سپس گزینه NuGet Package Manger و انتخاب کردن Package Manger Console صفحه Package Manger Console را باز کرده و سپس دستور زیر را در آن تایپ کنید.

PM> Scaffold-DbContext “Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;” Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models

پارامترهای دستور Scaffold-DbContext

در این دستور مشاهده می‌کنید که اولین پارامتر یک Connection String است که حاوی سه بخش می باشد. Database Server و Database Name و اطلاعات امنیتی. در Connection String مربوطه قسمت Server=.\SQLExpress; به این معنی است که Database Server به صورت Local و بر روی SQLEXPRESS تنظیم شده است. نام دیتابیس SchoolDB است و همچنین اطلاعات امنیتی با مقدار Trusted_Connection=True; تنظیم شده است و این به این معنی است که از Windows Authentication استفاده می شود. به عبارت دیگر از Username و Password ویندوز یا اصطلاحاً Windows Credentials برای متصل شدن به SQL Server استفاده می‌ شود.

پارامتر دوم دستور Scaffold-DbContext نام Database Provider است که با مقدار Microsoft.EntityFrameworkCore.SqlServer تنظیم شده است و اما پارامتر بعدی -OutputDir است که با نام فولدری که قصد داریم تمامی کلاس های تولید شده در آن قرار بگیرند تنظیم می‌شود. در این مثال ما فولدر Models از پروژه را برای انجام این کار استفاده کرده‌ایم. و اما اگر جزئیات بیشتری مربوط به دستور Scaffold-DbContext را میخواهید از دستور زیر استفاده کنید.

PM> get-help scaffold-dbcontext –detailed

دستور Scaffold-DbContext که در قسمت بالا مشاهده کردید باعث ساخته شدن Entity Class هایی برای هر کدام از Table های دیتابیس SchoolDB می‌ شود. علاوه بر این کلاس Context با ارث بری کردن از DbContext ساخته می شود.



به منظور انجام پیکر بندی ها در کلاس DbContext از Fluent API استفاده می شود و تمامی Entity Class هایی که در فولدر Models قرار گرفتند پیکربندی می‌شوند. در قسمت پایین Entity Class ای با نام Student که برای جدول Student در دیتابیس ساخته شده است را مشاهده می کنید.


using System;

using System.Collections.Generic;

namespace EFCoreTutorials.Models

{

public partial class Student

{

public Student()

{

StudentCourse = new HashSet<StudentCourse>();

}

public int StudentId { get; set; }

public string FirstName { get; set; }

public string LastName { get; set; }

public int? StandardId { get; set; }

public Standard Standard { get; set; }

public StudentAddress StudentAddress { get; set; }

public ICollection<StudentCourse> StudentCourse { get; set; }

}

}

در قسمت زیر کلاس SchoolDBContext را مشاهده می‌کنید که برای بازیابی کردن و ذخیره کردن داده ها از و بر روی دیتابیس مورد استفاده قرار می گیرد.

using System;

using Microsoft.EntityFrameworkCore;

using Microsoft.EntityFrameworkCore.Metadata;

namespace EFCoreTutorials.Models

{

public partial class SchoolDBContext : DbContext

{

public virtual DbSet<Course> Course { get; set; }

public virtual DbSet<Standard> Standard { get; set; }

public virtual DbSet<Student> Student { get; set; }

public virtual DbSet<StudentAddress> StudentAddress { get; set; }

public virtual DbSet<StudentCourse> StudentCourse { get; set; }

public virtual DbSet<Teacher> Teacher { get; set; }

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)

{

if (!optionsBuilder.IsConfigured)

{

#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.

optionsBuilder.UseSqlServer(@"Server=.\SQLExpress;Database=SchoolDB;Trusted_Connection=True;");

}

}

protected override void OnModelCreating(ModelBuilder modelBuilder)

{

modelBuilder.Entity<Course>(entity =>

{

entity.Property(e => e.CourseName)

.HasMaxLength(50)

.IsUnicode(false);

entity.HasOne(d => d.Teacher)

.WithMany(p => p.Course)

.HasForeignKey(d => d.TeacherId)

.OnDelete(DeleteBehavior.Cascade)

.HasConstraintName("FK_Course_Teacher");

});

modelBuilder.Entity<Standard>(entity =>

{

entity.Property(e => e.Description)

.HasMaxLength(50)

.IsUnicode(false);

entity.Property(e => e.StandardName)

.HasMaxLength(50)

.IsUnicode(false);

});

modelBuilder.Entity<Student>(entity =>

{

entity.Property(e => e.StudentId).HasColumnName("StudentID");

entity.Property(e => e.FirstName)

.HasMaxLength(50)

.IsUnicode(false);

entity.Property(e => e.LastName)

.HasMaxLength(50)

.IsUnicode(false);

entity.HasOne(d => d.Standard)

.WithMany(p => p.Student)

.HasForeignKey(d => d.StandardId)

.OnDelete(DeleteBehavior.Cascade)

.HasConstraintName("FK_Student_Standard");

});

modelBuilder.Entity<StudentAddress>(entity =>

{

entity.HasKey(e => e.StudentId);

entity.Property(e => e.StudentId)

.HasColumnName("StudentID")

.ValueGeneratedNever();

entity.Property(e => e.Address1)

.IsRequired()

.HasMaxLength(50)

.IsUnicode(false);

entity.Property(e => e.Address2)

.HasMaxLength(50)

.IsUnicode(false);

entity.Property(e => e.City)

.IsRequired()

.HasMaxLength(50)

.IsUnicode(false);

entity.Property(e => e.State)

.IsRequired()

.HasMaxLength(50)

.IsUnicode(false);

entity.HasOne(d => d.Student)

.WithOne(p => p.StudentAddress)

.HasForeignKey<StudentAddress>(d => d.StudentId)

.HasConstraintName("FK_StudentAddress_Student");

});

modelBuilder.Entity<StudentCourse>(entity =>

{

entity.HasKey(e => new { e.StudentId, e.CourseId });

entity.HasOne(d => d.Course)

.WithMany(p => p.StudentCourse)

.HasForeignKey(d => d.CourseId)

.OnDelete(DeleteBehavior.ClientSetNull)

.HasConstraintName("FK_StudentCourse_Course");

entity.HasOne(d => d.Student)

.WithMany(p => p.StudentCourse)

.HasForeignKey(d => d.StudentId)

.HasConstraintName("FK_StudentCourse_Student");

});

modelBuilder.Entity<Teacher>(entity =>

{

entity.Property(e => e.StandardId).HasDefaultValueSql("((0))");

entity.Property(e => e.TeacherName)

.HasMaxLength(50)

.IsUnicode(false);

entity.HasOne(d => d.Standard)

.WithMany(p => p.Teacher)

.HasForeignKey(d => d.StandardId)

.OnDelete(DeleteBehavior.Cascade)

.HasConstraintName("FK_Teacher_Standard");

});

}

}

}

کته بسیار مهم اینکه Entity Framework Core فقط بر اساس Table های موجود در دیتابیس Entity Class ها را و نه برای Stored Procedure یا View ها. به عبارت دیگر برای هر Table از بانک اطلاعاتی یک Entity Class ساخته می شود.

انجام دستور Scaffold-DbContext با استفاده از DotNet CLI

اگر از DotNet CLI که مخفف Command Line Interface می باشد به منظور اجرا کردن دستورات Entity Framework Core استفاده می ‌کنید می ‌توانید Command Prompt را باز کنید و با Navigate کردن به فولدر root و اجرا کردن دستور زیر از دیتابیس یک Model بسازید.

نکته بسیار مهم اینکه پس از ساختن Model با استفاده از این روش می توانید با استفاده از دستورات مربوط به Migration تغییراتی که بر روی Model ایجاد می کنید را بر روی دیتابیس اعمال کرده و هر دوی اینها را با همدیگر Sync کنید.