کلاسهای موجودیتهای مثال جاری
برای توضیح قابلیت جدید مقدار دهی اولیهی بانک اطلاعاتی در +EF Core 2.1، از کلاسهای موجودیتهای ذیل استفاده خواهیم کرد:
public class Magazine { public int MagazineId { get; set; } public string Name { get; set; } public string Publisher { get; set; } public List<Article> Articles { get; set; } } public class Article { public int ArticleId { get; set; } public string Title { get; set; } public DateTime PublishDate { get; set; } public int MagazineId { get; set; } public Author Author { get; set; } public int? AuthorId { get; set; } } public class Author { public int AuthorId { get; set; } public string Name { get; set; } public List<Article> Articles { get; set; } }
روش مقدار دهی اولیهی تک موجودیتها
اکنون فرض کنید قصد داریم جدول مجلات را مقدار دهی اولیه کنیم. برای اینکار خواهیم داشت:
protected override void OnModelCreating (ModelBuilder modelBuilder) { modelBuilder.Entity<Magazine>().HasData(new Magazine { MagazineId = 1, Name = "DNT Magazine" }); }
- ذکر صریح مقدار Id یک رکورد (هرچند نوع Id آن auto-increment است).
- عدم ذکر مقدار Publisher.
اکنون اگر توسط دستورات Migrations مانند dotnet ef migrations add init، کار تولید کدهای متناظر به روز رسانی بانک اطلاعاتی را بر اساس این کدها تولید کنیم، در قسمتی از آن، یک چنین خروجی را دریافت خواهیم کرد:
migrationBuilder.InsertData( table: "Magazines", columns: new[] { "MagazineId", "Name", "Publisher" }, values: new object[] { 1, "DNT Magazine", null });
set IDENTITY_INSERT ON INSERT INTO "Magazines" ("MagazineId", "Name", "Publisher") VALUES (1, 'DNT Magazine', NULL);
توسط متد HasData امکان درج چندین رکورد با هم نیز وجود دارد:
modelBuilder.Entity<Magazine>() .HasData(new Magazine{ MagazineId=2, Name="This Mag" }, new Magazine{ MagazineId=3, Name="That Mag" } );
البته باید دقت داشت که متد HasData، برای کار با یک تک موجودیت، طراحی شدهاست و توسط آن نمیتوان در چندین جدول بانک اطلاعاتی، مقادیری را درج کرد.
در مورد دادههای نالنپذیر چطور؟
در مثال فوق اگر تنظیمات خاصیت Publisherای را که نال وارد کردیم، نالنپذیر تعریف کنیم:
modelBuilder.Entity<Magazine>().Property(m=>m.Publisher).IsRequired();
"The seed entity for entity type 'Magazine' cannot be added because there was no value provided for the required property 'Publisher'."
امکان استفادهی از Anonymous Types در متد HasData
فرض کنید برای کلاس موجودیت خود یک سازنده را نیز تعریف کردهاید:
public Magazine(string name, string publisher) { Name=name; Publisher=publisher; }
modelBuilder.Entity<Magazine>().HasData(new Magazine("DNT Magazine", "1105 Media"));
modelBuilder.Entity<Magazine>().HasData(new {MagazineId=1, Name="DNT Mag", Publisher="1105 Media"});
migrationBuilder.InsertData( table: "Magazines", columns: new[] { "MagazineId", "Name", "Publisher" }, values: new object[] { 1, "DNT Mag", "1105 Media" });
حالت دیگر استفادهی از این قابلیت، کار با خواصی هستند که private set میباشند. فرض کنید کلاس موجودیت Magazine را به صورت زیر تغییر دادهاید:
public class Magazine { public Magazine(string name, string publisher) { Name=name; Publisher=publisher; MagazineId=Guid.NewGuid(); } public Guid MagazineId { get; private set; } public string Name { get; private set; } public string Publisher { get; private set; } public List<Article> Articles { get; set; } }
modelBuilder.Entity<Magazine>().HasData(new Magazine("DNT Mag", "1105 Media");
var mag1=new {MagazineId= new Guid("0483b59c-f7f8-4b21-b1df-5149fb57984e"), Name="DNT Mag", Publisher="1105 Media"}; modelBuilder.Entity<Magazine>().HasData(mag1);
مقدار دهی اولیهی اطلاعات به هم مرتبط
همانطور که پیشتر نیز ذکر شد، متد HasData تنها با یک تک موجودیت کار میکند و روش کار آن همانند کار با DbSetها نیست. به همین جهت نمیتوان اشیاء به هم مرتبط را توسط آن در بانک اطلاعاتی درج کرد. بنابراین برای درج اطلاعات یک مجله و مقالات مرتبط با آن، ابتدا باید مجله را ثبت کرد و سپس بر اساس Id آن مجله، کلید خارجی مقالات را به صورت جداگانهای مقدار دهی نمود:
modelBuilder.Entity<Article>().HasData(new Article { ArticleId = 1, MagazineId = 1, Title = "EF Core 2.1 Query Types"});
var mag1=new {MagazineId= new Guid("0483b59c-f7f8-4b21-b1df-5149fb57984e"), Name="DNT Mag", Publisher="1105 Media"}; modelBuilder.Entity<Magazine>().HasData(mag1);
مقدار دهی اولیهی Owned Entities
complex types در EF 6x با مفهوم دیگری به نام owned types در EF Core جایگزین شدهاند:
public class Publisher { public string Name { get; set; } public int YearFounded { get; set; } } public class Magazine { public int MagazineId { get; set; } public string Name { get; set; } public Publisher Publisher { get; set; } public List<Article> Articles { get; set; } }
modelBuilder.Entity<Magazine>().HasData (new Magazine { MagazineId = 1, Name = "DNT Magazine" }); modelBuilder.Entity<Magazine>().OwnsOne (m => m.Publisher) .HasData (new { Name = "1105 Media", YearFounded = 2006, MagazineId=1 });
این دو دستور، خروجی Migrations زیر را تولید میکنند:
migrationBuilder.InsertData( table: "Magazines", columns: new[] { "MagazineId", "Name", "Publisher_Name", "Publisher_YearFounded" }, values: new object[] { 1, "DNT Magazine", "1105 Media", 2006 });
محل صحیح اجرای Migrations در برنامههای ASP.NET Core 2x
زمانیکه متد ()context.Database.Migrate را اجرا میکنید، تمام مهاجرتهای اعمال نشده را به بانک اطلاعاتی اعمال میکند که این مورد شامل اجرای دستورات HasData نیز هست. روش فراخوانی این متد در ASP.NET Core 1x به صورت زیر در متد Configure کلاس Startup بود (و البته هنوز هم کار میکند):
namespace EFCoreMultipleDb.Web { public class Startup { public void Configure(IApplicationBuilder app, IHostingEnvironment env) { applyPendingMigrations(app); // ... } private static void applyPendingMigrations(IApplicationBuilder app) { var scopeFactory = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>(); using (var scope = scopeFactory.CreateScope()) { var uow = scope.ServiceProvider.GetService<IUnitOfWork>(); uow.Migrate(); } } } }
namespace EFCoreMultipleDb.DataLayer.SQLite.Context { public class SQLiteDbContext : DbContext, IUnitOfWork { // ... public void Migrate() { this.Database.Migrate(); } } }
public static void Main(string[] args) { var host = BuildWebHost(args); using (var scope = host.Services.CreateScope()) { var context = scope.ServiceProvider.GetRequiredService<yourDBContext>(); context.Database.Migrate(); } host.Run(); }