前兩篇博文中的配置屬性和配置關系都是配置映射,配置屬性是屬性的映射,配置關系式關系的映射,本篇從講講實體的映射。
首先,配置實體映射到表,使用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: }
測試結果:
二、配置一個實體到多個表
拿用戶和用戶信息來說,在映射到已有的數據庫時,有可能用戶包含的信息比較多,為了性能或其他一些原因,不經常用的用戶信息存放到單獨的表中,經常使用的信息則存放到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: }
測試結果:
三、繼承
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: }
測試結果:
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表,只包含基類中的屬性:
PictureBlogs表,只包含派生類中的屬性:
VideoBlogs表:
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: }
現在再運行程序,會出現下面的錯誤:
出現這個問題的主要問題是因為不支持多態關聯。歸根到底,就是在數據庫中關聯表示為外鍵關系,在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表:
VideoBlogs表:
關於TPC的內容,可以查看這篇文章:Inheritance with EF Code First: Part 3 – Table per Concrete Type (TPC)
四、結束語
點擊查看《Entity Framework實例詳解》系列的其他文章。
如果遇到問題,可以訪問Entity Framework社區,網址是www.ef-community.com。