• 【配置映射】—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

    作者:BobTian
    出处http://nianming.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
    欢迎访问我的个人博客:程序旅途
  • 相关阅读:
    心情日记:【原创诗歌】怆情吟
    心情日记:2008年3月3日 奶奶去世
    心情日记:健身日记
    金融基础概念期货
    FXDD点值获利计算
    外汇基础概念汇率
    报告论文:是学生都copy下来,现在不用,将来绝对要用(转)
    情感日记:毕业临走物语
    美元为什么坚挺
    英特尔首席技术官:人机智能鸿沟将于2050年消失
  • 原文地址:https://www.cnblogs.com/nianming/p/2778256.html
Copyright © 2020-2023  润新知