前言
最近在研讀 ASP.NET MVC 5 : 網站開發美學 4.6.3 / 4.6.4 / 4.8 時, 將書中範例作了一次演練整合, 包含以下內容;
- 定義或變更欄位的資料庫型態
- 加入欄位
- 1 to 1 關聯
- 1 to many 關聯
- many to many 關聯
- 資料庫移轉 (Database Migration)
在閱讀本文的同時, 可以參考 ASP.NET MVC 5 : 網站開發美學 書中的章節作對照.
過程
- 1. 建立 Console 專案
- 2. 加入 ADO.NET 實體資料模型 --> 空的 Code First 模型 (BlogModel)
- 3. 進行 Initial 資料庫的建立
- 加入 Blog.cs / BlogArticle.cs ( 1 : n )
- public class Blog
- {
- public int Id { get; set; }
- public Guid OwnerId { get; set; }
- public string Caption { get; set; }
- public DateTime DateCreated { get; set; }
- }
- public class BlogArticle
- {
- public Guid Id { get; set; }
- public int BlogId { get; set; }
- public string Subject { get; set; }
- public string Body { get; set; }
- public DateTime DateCreated { get; set; }
- public DateTime DateModified { get; set; }
- }
- 修改 BlogModel.cs
- public virtual DbSet<Blog> Blogs { get; set; }
- public virtual DbSet<BlogArticle> BlogArticles { get; set; }
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- base.OnModelCreating(modelBuilder);
- }
- 加入 MyDbInitializer 類別 (in Program.cs)
- class MyDbInitializer : IDatabaseInitializer<BlogModel>
- {
- public void InitializeDatabase(BlogModel context)
- {
- //context.Database.Delete();
- context.Database.CreateIfNotExists();
- }
- }
- 修改 Program.cs
- static void Main(string[] args)
- {
- using (BlogModel context = new BlogModel() )
- {
- // 方式一:
- // context.Database.CreateIfNotExists(); //如果資料庫不存在, 就建立
- // 方式二: 透過 IDatabaseInitializer<T> 的實作
- Database.SetInitializer(new MyDbInitializer()); // set initializer
- context.Database.Initialize(true); // run initializer, 這裡才會跑 BlogModel.OnModelCreating()
- }
- }
- 執行程式, 會產生第一版的 DB
- 在 Package Manager Console 裡, 下達 PM> Enable-Migrations
- 4. 進行第2次資料庫移轉 (限制欄位長度)
- 修改 BlogModel.cs
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.Subject).HasMaxLength(250).IsRequired();
- blogArticleTable.Property(c => c.Body).HasMaxLength(4000).IsRequired();
- base.OnModelCreating(modelBuilder);
- }
- 在 Package Manager Console 裡, 下達 PM> Add-Migration AlterBlogArticle
- 資料庫還沒有變喔 ...
- 執行 PM> Update-Database
- 終於更改資料庫格式了
- 5. 進行第3次資料庫移轉 (加入欄位)
- BlogArticle.cs 加入一個 Language 屬性
- public class BlogArticle
- {
- public Guid Id { get; set; }
- public int BlogId { get; set; }
- public string Subject { get; set; }
- public string Body { get; set; }
- public DateTime DateCreated { get; set; }
- public DateTime DateModified { get; set; }
- public string Language { get; set; }
- }
- PM> Add-Migration AlterBlogArticle
- 此時, Console 專案會自動建置
- PM> Update-Database
- 6. 進行第4次資料庫移轉 ( 加入一對無或一的關係 )
- 加入 BlogInfo.cs
- public class BlogInfo
- {
- public Guid Id { get; set; }
- public string Author { get; set; }
- // 每 1 筆 BlogInfo, 會有 1 筆 Blog (也有可能是 0 筆, 由程式決定)
- public Blog Blog { get; set; }
- }
- 修改 Blog.cs
- public class Blog
- {
- public int Id { get; set; }
- public Guid OwnerId { get; set; }
- public string Caption { get; set; }
- public DateTime DateCreated { get; set; }
- // 每 1 筆 Blog, 會有 1 筆 BlogInfo
- public BlogInfo Info { get; set; }
- }
- 修改 BlogModel.cs
- public virtual DbSet<BlogInfo> BlogInfos { get; set; }
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- var blogInfoTable = modelBuilder.Entity<BlogInfo>().ToTable("BlogInfos");
- // ================================
- // 修改資料庫對應的 DataType
- // ================================
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.Subject).HasMaxLength(250).IsRequired();
- blogArticleTable.Property(c => c.Body).HasMaxLength(4000).IsRequired();
- // ================================
- // 建立關聯性
- // ================================
- // PKEY
- blogTable.HasKey(c => c.Id);
- // --------------------------------
- // BlogInfos vs. Blogs = 1 vs. 0 or 1
- // --------------------------------
- //blogTable.HasRequired(c => c.Info).WithOptional();
- //http://www.entityframeworktutorial.net/code-first/configure-one-to-one-relationship-in-code-first.aspx
- blogInfoTable.HasOptional(c => c.Blog) // blogInfo has blog -> Optional
- .WithRequired(a => a.Info); // blog has blogInfo -> Required
- base.OnModelCreating(modelBuilder);
- }
- PM> Add-Migration AlterOneToOneRelation
- PM> Update-Database
- 修改 Program.cs 的 MyDbInitializer 類別
- class MyDbInitializer : IDatabaseInitializer<BlogModel>
- {
- private void OneToZero(BlogModel context)
- {
- // ---------------------------------
- // 1 to 0 or 1
- // ---------------------------------
- Guid blogInfoId = Guid.NewGuid();
- context.BlogInfos.Add(new BlogInfo()
- {
- Id = blogInfoId,
- Author = "Judy",
- Blog = null
- });
- blogInfoId = Guid.NewGuid();
- context.BlogInfos.Add(new BlogInfo()
- {
- Id = blogInfoId,
- Author = "Jasper",
- Blog = new Blog() { Id = 1, Caption = "Travel Blog Site", OwnerId = Guid.NewGuid(), DateCreated = DateTime.Now }
- });
- }
- public void InitializeDatabase(BlogModel context)
- {
- //context.Database.Delete();
- context.Database.CreateIfNotExists();
- //以下放一些初始資料新增
- // ---------------------------------
- // 1 to 0 or 1
- // ---------------------------------
- this.OneToZero(context);
- context.SaveChanges();
- }
- }
- 執行程式
- 註: 還原至某一個版本 (http://stackoverflow.com/questions/11904571/ef-migrations-rollback-last-applied-migration)
- PM> Update-Database -TargetMigration:"AlterBlogArticle1"
- 7. 進行第5次資料庫移轉 ( 加入一對一的關係 )
- 修改 BlogModel.cs
- public virtual DbSet<BlogInfo> BlogInfos { get; set; }
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- var blogInfoTable = modelBuilder.Entity<BlogInfo>().ToTable("BlogInfos");
- // ================================
- // 修改資料庫對應的 DataType
- // ================================
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.Subject).HasMaxLength(250).IsRequired();
- blogArticleTable.Property(c => c.Body).HasMaxLength(4000).IsRequired();
- // ================================
- // 建立關聯性
- // ================================
- // PKEY
- blogTable.HasKey(c => c.Id);
- blogInfoTable.HasKey(c => c.Id);
- //// --------------------------------
- //// BlogInfos vs. Blogs = 1 vs. 0 or 1
- //// --------------------------------
- ////blogTable.HasRequired(c => c.Info).WithOptional();
- //blogInfoTable.HasOptional(c => c.Blog) // blogInfo has blog -> Optional
- // .WithRequired(a => a.Info); // blog has blogInfo -> Required
- // --------------------------------
- // BlogInfos vs. Blogs = 1 vs. 1
- // --------------------------------
- //blogTable.HasRequired(c => c.Info).WithRequiredPrincipal(c => c.Blog);
- blogInfoTable.HasRequired(c => c.Blog) // blogInfo has blog -> Required
- .WithRequiredPrincipal(a => a.Info); // blog has blogInfo -> Required
- base.OnModelCreating(modelBuilder);
- }
- PM> Add-Migration AlterOneToOneRelation2
- PM> Update-Database
- 修改 Program.cs 的 MyDbInitializer 類別
- private void OneToOne(BlogModel context)
- {
- // ---------------------------------
- // 1 to 1
- // ---------------------------------
- Guid blogInfoId = Guid.NewGuid();
- context.BlogInfos.Add(new BlogInfo()
- {
- Id = blogInfoId,
- Author = "Jasper",
- Blog = new Blog() { Id = 2, Caption = "Travel Blog Site2", OwnerId = Guid.NewGuid(), DateCreated = DateTime.Now }
- });
- }
- public void InitializeDatabase(BlogModel context)
- {
- //context.Database.Delete();
- context.Database.CreateIfNotExists();
- //以下放一些初始資料新增
- //// ---------------------------------
- //// 1 to 0 or 1
- //// ---------------------------------
- //this.OneToZero(context);
- // ---------------------------------
- // 1 to 1
- // ---------------------------------
- this.OneToOne(context);
- context.SaveChanges();
- }
- 執行程式
- 8. 進行第6次資料庫移轉 ( 加入一對多的關係 )
- 修改 Blog.cs
- public class Blog
- {
- public int Id { get; set; }
- public Guid OwnerId { get; set; }
- public string Caption { get; set; }
- public DateTime DateCreated { get; set; }
- // 每 1 筆 Blog, 會有 1 筆 BlogInfo
- public BlogInfo Info { get; set; }
- // 每一個 Blog Site, 會有多篇文章
- public ICollection<BlogArticle> Articles { get; set; }
- }
- 修改 BlogArticle.cs
- public class BlogArticle
- {
- public Guid Id { get; set; }
- public int BlogId { get; set; }
- public string Subject { get; set; }
- public string Body { get; set; }
- public DateTime DateCreated { get; set; }
- public DateTime DateModified { get; set; }
- public string Language { get; set; }
- // 每一篇文章都屬於一個 Blog Site
- public Blog Blog { get; set; }
- }
- 修改 BlogModel.cs
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- var blogInfoTable = modelBuilder.Entity<BlogInfo>().ToTable("BlogInfos");
- // ================================
- // 修改資料庫對應的 DataType
- // ================================
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.Subject).HasMaxLength(250).IsRequired();
- blogArticleTable.Property(c => c.Body).HasMaxLength(4000).IsRequired();
- // ================================
- // 建立關聯性
- // ================================
- // PKEY
- blogTable.HasKey(c => c.Id);
- blogInfoTable.HasKey(c => c.Id);
- //// --------------------------------
- //// BlogInfos vs. Blogs = 1 vs. 0 or 1
- //// --------------------------------
- ////blogTable.HasRequired(c => c.Info).WithOptional();
- //blogInfoTable.HasOptional(c => c.Blog) // blogInfo has blog -> Optional
- // .WithRequired(a => a.Info); // blog has blogInfo -> Required
- // --------------------------------
- // BlogInfos vs. Blogs = 1 vs. 1
- // --------------------------------
- //blogTable.HasRequired(c => c.Info).WithRequiredPrincipal(c => c.Blog);
- blogInfoTable.HasRequired(c => c.Blog) // blogInfo has blog -> Required
- .WithRequiredPrincipal(a => a.Info); // blog has blogInfo -> Required
- // --------------------------------
- // Blogs vs. BlogArticles = 1 vs. n
- // --------------------------------
- blogTable.HasMany(c => c.Articles) // Blog has many Articles -> Required
- .WithRequired(a => a.Blog); // Articles has Blog -> Required
- base.OnModelCreating(modelBuilder);
- }
- PM> Add-Migration AlterOneToManyRelation
- PM> Update-Database
- 修改 Program.cs 的 MyDbInitializer 類別
- private void OneToMany(BlogModel context)
- {
- // ---------------------------------
- // 1 to n
- // ---------------------------------
- Guid blogInfoId = Guid.NewGuid();
- context.BlogInfos.Add(new BlogInfo()
- {
- Id = blogInfoId,
- Author = "Jasper",
- Blog = new Blog()
- {
- Id = 3,
- Caption = "Travel Blog Site3",
- OwnerId = Guid.NewGuid(),
- DateCreated = DateTime.Now,
- Articles = new List<BlogArticle>()
- {
- new BlogArticle() { Id = Guid.NewGuid(), BlogId = 3, Subject = "Japan", Body = "Tokyo", Language="中文", DateCreated = DateTime.Now, DateModified = DateTime.Now },
- new BlogArticle() { Id = Guid.NewGuid(), BlogId = 3, Subject = "Japan", Body = "Kyoto", Language="中文", DateCreated = DateTime.Now, DateModified = DateTime.Now }
- }
- }
- });
- }
- public void InitializeDatabase(BlogModel context)
- {
- //context.Database.Delete();
- context.Database.CreateIfNotExists();
- //以下放一些初始資料新增
- //// ---------------------------------
- //// 1 to 0 or 1
- //// ---------------------------------
- //this.OneToZero(context);
- //// ---------------------------------
- //// 1 to 1
- //// ---------------------------------
- //this.OneToOne(context);
- // ---------------------------------
- // 1 to n
- // ---------------------------------
- this.OneToMany(context);
- context.SaveChanges();
- }
- 執行程式
- 9. 進行第7次資料庫移轉 ( 加入多對多的關係 )
- 加入 BlogFile.cs
- 1個檔案 (BlogFile), 可以被篇文章 (BlogArticle) 使用; 1篇文章 (BlogArticle), 可以包含多個檔案 (BlogFile)
- public class BlogFile
- {
- public Guid Id { get; set; }
- public string Name { get; set; }
- //1個檔案, 可以被篇文章 (article) 使用
- public ICollection<BlogArticle> Articles { get; set; }
- }
- 修改 BlogArticle.cs
- public class BlogArticle
- {
- public Guid Id { get; set; }
- public int BlogId { get; set; }
- public string Subject { get; set; }
- public string Body { get; set; }
- public DateTime DateCreated { get; set; }
- public DateTime DateModified { get; set; }
- public string Language { get; set; }
- // 每一篇文章都屬於一個 Blog Site
- public Blog Blog { get; set; }
- //1篇文章, 可以包含多個檔案
- public ICollection<BlogFile> Files { get; set; }
- }
- 修改 BlogModel.cs
- public virtual DbSet<BlogFile> BlogFiles { get; set; }
- protected override void OnModelCreating(DbModelBuilder modelBuilder)
- {
- var blogTable = modelBuilder.Entity<Blog>().ToTable("Blogs");
- var blogArticleTable = modelBuilder.Entity<BlogArticle>().ToTable("BlogArticles");
- var blogInfoTable = modelBuilder.Entity<BlogInfo>().ToTable("BlogInfos");
- var blogFileTable = modelBuilder.Entity<BlogFile>().ToTable("BlogFiles");
- // ================================
- // 修改資料庫對應的 DataType
- // ================================
- blogTable.Property(c => c.Id).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.BlogId).IsRequired().HasColumnType("bigint");
- blogArticleTable.Property(c => c.Subject).HasMaxLength(250).IsRequired();
- blogArticleTable.Property(c => c.Body).HasMaxLength(4000).IsRequired();
- // ================================
- // 建立關聯性
- // ================================
- // PKEY
- blogTable.HasKey(c => c.Id);
- blogInfoTable.HasKey(c => c.Id);
- blogFileTable.HasKey(c => c.Id);
- //// --------------------------------
- //// BlogInfos vs. Blogs = 1 vs. 0 or 1
- //// --------------------------------
- ////blogTable.HasRequired(c => c.Info).WithOptional();
- //blogInfoTable.HasOptional(c => c.Blog) // blogInfo has blog -> Optional
- // .WithRequired(a => a.Info); // blog has blogInfo -> Required
- // --------------------------------
- // BlogInfos vs. Blogs = 1 vs. 1
- // --------------------------------
- //blogTable.HasRequired(c => c.Info).WithRequiredPrincipal(c => c.Blog);
- blogInfoTable.HasRequired(c => c.Blog) // blogInfo has blog -> Required
- .WithRequiredPrincipal(a => a.Info); // blog has blogInfo -> Required
- // --------------------------------
- // Blogs vs. BlogArticles = 1 vs. n
- // --------------------------------
- blogTable.HasMany(c => c.Articles) // Blog has many Articles -> Required
- .WithRequired(a => a.Blog); // Articles has Blog -> Required
- // --------------------------------
- // BlogArticles vs. BlogFiles = m vs. n
- // --------------------------------
- // 多對多: 在關聯式資料庫不存在, 會多產生一個 Table, 內含雙方的 PKey, 以造成 2 個 一對多 的關係
- blogArticleTable.HasMany(c => c.Files) // BlogArticle has many BlogFile -> Required
- .WithMany(a => a.Articles) // BlogFile has many BlogAritcle -> Required
- .Map
- ( m =>
- {
- m.ToTable("BlogArticlesFiles");
- m.MapLeftKey("BlogArticlesId");
- m.MapRightKey("BlogFilesId");
- }
- );
- base.OnModelCreating(modelBuilder);
- }
- PM> Add-Migration AlterManyToManyRelation
- PM> Update-Database
- 修改 Program.cs 的 MyDbInitializer 類別
- private void ManyToMany(BlogModel context)
- {
- // ---------------------------------
- // m to n
- // ---------------------------------
- Guid blogInfoId = Guid.NewGuid();
- //建立多個檔案
- BlogFile file1 = new BlogFile() { Id = Guid.NewGuid(), Name = "BMP檔" };
- BlogFile file2 = new BlogFile() { Id = Guid.NewGuid(), Name = "JPG檔" };
- BlogFile file3 = new BlogFile() { Id = Guid.NewGuid(), Name = "TXT檔" };
- context.BlogInfos.Add(new BlogInfo()
- {
- Id = blogInfoId,
- Author = "Jasper",
- Blog = new Blog()
- {
- Id = 4,
- Caption = "Travel Blog Site4",
- OwnerId = Guid.NewGuid(),
- DateCreated = DateTime.Now,
- Articles = new List<BlogArticle>()
- {
- new BlogArticle() { Id = Guid.NewGuid(), BlogId = 4, Subject = "Taiwan", Body = "Taipei", Language="中文", DateCreated = DateTime.Now, DateModified = DateTime.Now,
- Files = new List<BlogFile>(){ file1, file2 } },
- new BlogArticle() { Id = Guid.NewGuid(), BlogId = 4, Subject = "Taiwan", Body = "Tainan", Language="中文", DateCreated = DateTime.Now, DateModified = DateTime.Now,
- Files = new List<BlogFile>(){ file2, file3 } },
- new BlogArticle() { Id = Guid.NewGuid(), BlogId = 4, Subject = "Taiwan", Body = "Taoyuan", Language="中文", DateCreated = DateTime.Now, DateModified = DateTime.Now,
- Files = new List<BlogFile>(){ file3, file1 } }
- }
- }
- });
- }
- public void InitializeDatabase(BlogModel context)
- {
- //context.Database.Delete();
- context.Database.CreateIfNotExists();
- //以下放一些初始資料新增
- //// ---------------------------------
- //// 1 to 0 or 1
- //// ---------------------------------
- //this.OneToZero(context);
- //// ---------------------------------
- //// 1 to 1
- //// ---------------------------------
- //this.OneToOne(context);
- //// ---------------------------------
- //// 1 to n
- //// ---------------------------------
- //this.OneToMany(context);
- // ---------------------------------
- // m to n
- // ---------------------------------
- this.ManyToMany(context);
- context.SaveChanges();
- }
- 執行程式
參考文件
- 許薰尹: 使用Entity-Framework-6-Code-First與新資料庫建立應用程式
- Code First for New Database
- 許薰尹: 使用EntityFramework 6 Code First與既有資料庫建立應用程式
- Code First from Existing Database
- Entity Framework Tutorial: Entity Relationships
- 介紹 Entity 之間的關聯性
- 1 to 1
- 1 to many
- many to many
- Entity Framework Tutorial: Configure One-to-One Relationship
- 如何設置 1 to 1 的關聯 (Code First)
- Entity Framework Tutorial: Configure One-to-Many Relationship
- 如何設置 1 to many 的關聯 (Code First)
- Entity Framework Tutorial: Configure Many-to-Many Relationship
- 如何設置 many to many 的關聯 (Code First)
- Entity Framework Tutorial: Add New Entity using DBContext in Disconnected Scenario
- Entity Framework Tutorial: Update Existing Entity using DBContext in Disconnected Scenario
- Entity Framework Tutorial: Delete Entity using DBContext in Disconnected Scenario
- Code Project: Create Primary Key using Entity Framework Code First
- 在 Code First 的狀況下, 共有以下3種方式, 可以設定主鍵 (Primary Key):
- Convention: Property with name "Id" or property with name {class name} + "Id" (both are case insensitive)
- Data Annotation: [Key] attribute
- Fluent API: Entity<T>.HasKey(p => p.PropertyName) function
- 以下為複合鍵 (Composite Key) 的設定範例
// Data Annotation Attribute
public class Passport
{[Key]
[Column(Order = 10)]
public string PassportNumber { get; set; }
[Key]
[Column(Order = 20)]
public string CountryCode { get; set; }
}
// Fluent API
modelBuilder.Entity<Passport>().HasKey(s => new { s.PassportNumber, s.CountryCode});
- Stack Overflow: Mapping composite keys using EF code first
沒有留言:
張貼留言