【配置映射】—Entity Framework實例詳解


前兩篇博文中的配置屬性和配置關系都是配置映射,配置屬性是屬性的映射,配置關系式關系的映射,本篇從講講實體的映射。

首先,配置實體映射到表,使用ToTable方法,它接受兩個參數,第一個參數是表的名稱,第二個參數是Schema名稱。

   1:              ToTable("Destination", "baga");

一、配置多個實體到一個表

下面是用到的類:

   1:      public class Blog
   2:      {
   3:          public int Id { get; set; }
   4:          public DateTime Creationdate { get; set; }
   5:          public string ShortDescription { get; set; }
   6:          public string Title { get; set; }
   7:          public virtual BlogLogo BlogLogo { get; set; }
   8:      }
   9:   
  10:      public class BlogLogo
  11:      {
  12:          public int Id { get; set; }
  13:          public byte[] Logo { get; set; }
  14:      }

映射實體到一個表中,實體需要遵循以下規則:

1. 實體之間必須是一對一的關系。

2. 實體必須共用主鍵。

使用Fluent API 配置Blog和BlogLogo映射到一個表,使用ToTable方法,如下:

   1:              modelBuilder.Entity<Blog>().ToTable("Blogs");
   2:              modelBuilder.Entity<BlogLogo>().ToTable("Blogs");

完整的Demo如下:

   1: public class Blog
   2: {
   3:     public int Id { get; set; }
   4:     public DateTime Creationdate { get; set; }
   5:     public string ShortDescription { get; set; }
   6:     public string Title { get; set; }
   7:     public virtual BlogLogo BlogLogo { get; set; }
   8: }
   9:  
  10: public class BlogLogo
  11: {
  12:     public int Id { get; set; }
  13:     public byte[] Logo { get; set; }
  14: }
  15:  
  16: public class BlogConfiguration : EntityTypeConfiguration<Blog>
  17: {
  18:     public BlogConfiguration()
  19:     {
  20:         ToTable("Blogs");
  21:         HasKey(x => x.Id);
  22:         Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
  23:         Property(x => x.Title).HasMaxLength(175);
  24:         HasRequired(x => x.BlogLogo).WithRequiredPrincipal();
  25:     }
  26: }
  27:  
  28: public class BlogLogoConfiguration : EntityTypeConfiguration<BlogLogo>
  29: {
  30:     public BlogLogoConfiguration()
  31:     {
  32:         ToTable("Blogs");
  33:         HasKey(x => x.Id);
  34:         Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
  35:     }
  36: }
  37:  
  38:  
  39: public class BlogContext : DbContext
  40: {
  41:     public DbSet<Blog> Blogs { get; set; }
  42:  
  43:     protected override void OnModelCreating(DbModelBuilder modelBuilder)
  44:     {
  45:         modelBuilder.Configurations.Add(new BlogConfiguration());
  46:         modelBuilder.Configurations.Add(new BlogLogoConfiguration());
  47:         base.OnModelCreating(modelBuilder);
  48:     }
  49:  
  50:     public IQueryable<T> Find<T>() where T : class
  51:     {
  52:         return this.Set<T>();
  53:     }
  54:  
  55:     public void Refresh()
  56:     {
  57:         this.ChangeTracker.Entries().ToList().ForEach(x => x.Reload());
  58:     }
  59:     public void Commit()
  60:     {
  61:         this.SaveChanges();
  62:     }
  63: }
  64:  
  65: public class Initializer : DropCreateDatabaseAlways<BlogContext>
  66: {
  67:     public Initializer()
  68:     {
  69:     }
  70:  
  71:     protected override void Seed(BlogContext context)
  72:     {
  73:         context.Set<Blog>().Add(new Blog()
  74:         {
  75:             Creationdate = DateTime.Now,
  76:             ShortDescription = "Testing",
  77:             Title = "Test Blog",
  78:             BlogLogo = new BlogLogo() { Logo = new byte[0] }
  79:         });
  80:         context.SaveChanges();
  81:     }
  82: }

測試程序:

   1: [TestMethod]
   2: public void ShouldReturnABlogWithLogo()
   3: {
   4:     //Arrange
   5:     var init = new Initializer();
   6:     var context = new BlogContext();
   7:     init.InitializeDatabase(context);
   8:     //Act
   9:     var post = context.Blogs.FirstOrDefault();
  10:     //Assert
  11:     Assert.IsNotNull(post);
  12:     Assert.IsNotNull(post.BlogLogo);
  13: }

測試結果:

QQ截圖20121118211721

二、配置一個實體到多個表

拿用戶和用戶信息來說,在映射到已有的數據庫時,有可能用戶包含的信息比較多,為了性能或其他一些原因,不經常用的用戶信息存放到單獨的表中,經常使用的信息則存放到User表中,但是使用類表示時,希望將用戶所有的信息放到一個類中,在映射時,就需要將實體分割。

下面是使用到的類:

   1:      public class Blog
   2:      {
   3:          public int Id { get; set; }
   4:          public DateTime Creationdate { get; set; }
   5:          public string ShortDescription { get; set; }
   6:          public string Title { get; set; }
   7:          public string Description { get; set; }
   8:          public string AboutTheAuthor { get; set; }
   9:      }

使用Fluent API 將Blog個映射到多個表,使用Map方法,如下:

   1:              Map(m =>
   2:              {
   3:                  m.Properties(t => new { t.Id, t.Title, t.ShortDescription });
   4:                  m.ToTable("Blog");
   5:              })
   6:              .Map(m =>
   7:              {
   8:                  m.Properties(t => new { t.Description, t.Creationdate, t.AboutTheAuthor });
   9:                  m.ToTable("BlogDetails");
  10:              });

完整的Demo如下:

   1: public class Blog
   2: {
   3:     public int Id { get; set; }
   4:     public DateTime Creationdate { get; set; }
   5:     public string ShortDescription { get; set; }
   6:     public string Title { get; set; }
   7:     public string Description { get; set; }
   8:     public string AboutTheAuthor { get; set; }
   9: }
  10:  
  11: public class BlogConfiguration : EntityTypeConfiguration<Blog>
  12: {
  13:     public BlogConfiguration()
  14:     {
  15:         Map(m =>
  16:         {
  17:             m.Properties(t => new { t.Id, t.Title, t.ShortDescription });
  18:             m.ToTable("Blog");
  19:         })
  20:         .Map(m =>
  21:         {
  22:             m.Properties(t => new { t.Description, t.Creationdate, t.AboutTheAuthor });
  23:             m.ToTable("BlogDetails");
  24:         });
  25:         HasKey(x => x.Id);
  26:         Property(x => x.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
  27:     }
  28: }
  29:  
  30: public class BlogContext : DbContext, IUnitOfWork
  31: {
  32:     protected override void OnModelCreating(DbModelBuilder modelBuilder)
  33:     {
  34:         modelBuilder.Configurations.Add(new BlogConfiguration());
  35:         base.OnModelCreating(modelBuilder);
  36:     }
  37:     public DbSet<Blog> Blogs { get; set; }
  38:  
  39:     public IQueryable<T> Find<T>() where T : class
  40:     {
  41:         return this.Set<T>();
  42:     }
  43:  
  44:     public void Refresh()
  45:     {
  46:         this.ChangeTracker.Entries().ToList().ForEach(x => x.Reload());
  47:     }
  48:  
  49:     public void Commit()
  50:     {
  51:         this.SaveChanges();
  52:     }
  53: }
  54:  
  55: public interface IUnitOfWork
  56: {
  57:     IQueryable<T> Find<T>() where T : class;
  58:     void Refresh();
  59:     void Commit();
  60: }
  61:  
  62: public class Initializer : DropCreateDatabaseAlways<BlogContext>
  63: {
  64:     public Initializer()
  65:     {
  66:     }
  67:     protected override void Seed(BlogContext context)
  68:     {
  69:         context.Set<Blog>().Add(new Blog()
  70:         {
  71:             Creationdate = DateTime.Now,
  72:             ShortDescription = "Testing",
  73:             Title = "Test Blog",
  74:             Description = "Long Test",
  75:             AboutTheAuthor = "Me me me"
  76:         });
  77:         context.SaveChanges();
  78:     }
  79: }

測試程序:

   1: [TestMethod]
   2: public void ShouldReturnABlogWithAuthorDetails()
   3: {
   4:     //Arrange
   5:     var init = new Initializer();
   6:     var context = new BlogContext();
   7:     init.InitializeDatabase(context);
   8:     //Act
   9:     var post = context.Blogs.FirstOrDefault();
  10:     //Assert
  11:     Assert.IsNotNull(post);
  12:     Assert.IsNotNull(post.AboutTheAuthor);
  13: }

測試結果:

QQ截圖20121118220013

三、繼承

TPH,Table Per Hierarchy,就是基類和派生類都映射到一張表,使用辨別列區分。

TPT,Table Per Type,就是基類存放到一張主表,每個派生類存放到一張表,通過外鍵與主表關聯。

TPC,Table Per Concrete Type,就是基類和派生類都存放到單獨的表中,沒有主表。

下面是繼承部分使用到的類:

   1:      public class Blog
   2:      {
   3:          public int Id { get; set; }
   4:          public DateTime Creationdate { get; set; }
   5:          public string ShortDescription { get; set; }
   6:          public string Title { get; set; }
   7:          public string AboutTheAuthor { get; set; }
   8:      }
   9:   
  10:      public class PictureBlog : Blog
  11:      {
  12:          //想不起來用什么字段好了,弄個“圖片介紹”吧
  13:          public string PicDescription { get; set; }
  14:      }
  15:   
  16:      public class VideoBlog : Blog
  17:      {
  18:          //視頻介紹
  19:          public string VideoDescription { get; set; }
  20:      }

首先來看一下TPH

配置繼承為TPH,使用到一些新的配置方法:Requires和HasValue。Code First默認辨別列的名稱為Discriminator,辨別列的值為類的名稱。Requires配置辨別列的名稱,HasValue定義辨別列的值。

下面看TPH的Demo:

   1: [TestMethod]
   2: public void ShouldReturnABlogWithTypeSafety()
   3: {
   4: //Arrange
   5: var init = new Initializer();
   6: var context = new
   7: BlogContext(Settings.Default.BlogConnection);
   8: init.InitializeDatabase(context);
   9: //Act
  10: var pictureBlog =
  11: context.Set<PictureBlog>().FirstOrDefault();
  12: var videoBlog = context.Set<VideoBlog>().FirstOrDefault();
  13: //Assert
  14: Assert.IsNotNull(pictureBlog);
  15: Assert.IsNotNull(videoBlog);
  16: }
  17: }

測試程序:

   1: [TestMethod]
   2: public void ShouldReturnABlogWithTypeSafety()
   3: {
   4:     //Arrange
   5:     var init = new Initializer();
   6:     var context = new BlogContext();
   7:     init.InitializeDatabase(context);
   8:     //Act
   9:     var pictureBlog =context.Set<PictureBlog>().FirstOrDefault();
  10:     var videoBlog = context.Set<VideoBlog>().FirstOrDefault();
  11:     //Assert
  12:     Assert.IsNotNull(pictureBlog);
  13:     Assert.IsNotNull(videoBlog);
  14: }

測試結果:

QQ截圖20121119213918

QQ截圖20121119214034

TPT

配置繼承為TPT,只需使用ToTable顯示將派生類映射到表即可。

TPT的Demo,只需修改PictureBlog和VideoBlog的配置,其他全部一樣:

   1: public class PictureBlogConfiguration : EntityTypeConfiguration<PictureBlog>
   2: {
   3:     public PictureBlogConfiguration()
   4:     {
   5:         Map(m =>
   6:         {
   7:             m.ToTable("PictureBlogs");
   8:         });
   9:     }
  10: }
  11:  
  12: public class VideoBlogConfiguration : EntityTypeConfiguration<VideoBlog>
  13: {
  14:     public VideoBlogConfiguration()
  15:     {
  16:         Map(m =>
  17:         {
  18:             m.ToTable("VideoBlogs");
  19:         });
  20:     }
  21: }

最后生成的數據庫表如下圖所示:

Blogs表,只包含基類中的屬性:

QQ截圖20121119222136

PictureBlogs表,只包含派生類中的屬性:

QQ截圖20121119222153

VideoBlogs表:

QQ截圖20121119222203

TPC

TPC和TPT差不多,TPC使用MapInheritedProperties配置。MapInheritedProperties告訴Code First要映射基類中的屬性到派生類的表中。

代碼如下所示:

   1: public class BlogConfiguration : EntityTypeConfiguration<Blog>
   2: {
   3:     public BlogConfiguration()
   4:     {
   5:         ToTable("Blogs");
   6:         HasKey(t => t.Id);
   7:         Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnName("BlogId");
   8:         Property(t => t.Title).HasMaxLength(175);
   9:     }
  10: }
  11:  
  12: public class PictureBlogConfiguration : EntityTypeConfiguration<PictureBlog>
  13: {
  14:     public PictureBlogConfiguration()
  15:     {
  16:         Map(m =>
  17:         {
  18:             m.ToTable("PictureBlogs");
  19:             m.MapInheritedProperties();
  20:         });
  21:     }
  22: }

現在再運行程序,會出現下面的錯誤:

QQ截圖20121119232344

出現這個問題的主要問題是因為不支持多態關聯。歸根到底,就是在數據庫中關聯表示為外鍵關系,在TPC中,子類都映射到不同的表中,所以有多個關聯指向基類不能使用簡單的外鍵關系表示。

解決辦法:

配置Blog的主鍵產生之的方式為None,在創建對象時,手動給主鍵賦值。

   1:  public class BlogConfiguration : EntityTypeConfiguration<Blog>
   2:  {
   3:      public BlogConfiguration()
   4:      {
   5:          ToTable("Blogs");
   6:          HasKey(t => t.Id);
   7:          //配置不自動生成值
   8:          Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).HasColumnName("BlogId");
   9:          Property(t => t.Title).HasMaxLength(175);
  10:      }
  11:  }
   1: protected override void Seed(BlogContext context)
   2: {
   3:     context.Set<PictureBlog>().Add(new PictureBlog()
   4:     {
   5:         //手動給主鍵賦值
   6:         Id = 1,
   7:         Creationdate = DateTime.Now,
   8:         ShortDescription = "Testing",
   9:         Title = "Test Blog",
  10:         AboutTheAuthor = "Me me me",
  11:         PicDescription = "This is a picture description"
  12:     });
  13:     context.Set<VideoBlog>().Add(new VideoBlog()
  14:     {
  15:         Id = 2,
  16:         Creationdate = DateTime.Now,
  17:         ShortDescription = "Testing",
  18:         Title = "Test Blog",
  19:         AboutTheAuthor = "Me me me",
  20:         VideoDescription = "This is a video description"
  21:     });
  22:     context.SaveChanges();
  23: }
  24:     }

運行結果:

PictureBlogs表:

QQ截圖20121119234414

VideoBlogs表:

QQ截圖20121119234424

關於TPC的內容,可以查看這篇文章:Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)

四、結束語

點擊查看《Entity Framework實例詳解》系列的其他文章。

如果遇到問題,可以訪問Entity Framework社區,網址是www.ef-community.com


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM