【配置关系】—Entity Framework实例详解
实体间的关系,简单来说无非就是一对一、一对多、多对多,根据方向性来说又分为双向和单向。Code First在实体关系上有以下约定:
1. 两个实体,如果一个实体包含一个引用属性,另一个实体包含一个集合属性,Code First默认约定它们为一对多关系。
2. 两个实体,如果只有一个实体包含一个导航属性或一个集合属性,Code First也默认约定它们是一对多关系。
3. 两个实体分别包含一个集合属性,Code First默认约定它们为多对多关系。
4. 两个实体分别包含一个引用属性,Code First默认约定它们为一对一关系。
5. 在一对一关系情况下,需要提供给Code First额外的信息,以确定它们的主从关系。
6. 在实体中定义一个外键属性,Code First使用属性是否为空来确定关系是必须还是可选。
一、一对一
在Code First中,一对一关系总是需要配置,因为两个实体都包含有一个引用属性,无法确定它们的主从关系。
配置一对一关系常用的方法:
HasRequired ,HasOptional ,WithOptional ,WithRequiredPrincipal,WithRequiredDependent
下面是用到的类:
public class Person { public int PersonId { get; set; } public int SocialSecurityNumber { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public byte[] RowVersion { get; set; } public PersonPhoto Photo { get; set; } } public class PersonPhoto { public int PersonId { get; set; } public byte[] Photo { get; set; } public string Caption { get; set; } public Person PhotoOf { get; set; } }
因为Photo是具体人的,所以PersonPhoto使用PersonId作为主键。
下面是一对一关系配置的几种情况:
1.PersonPhoto必须属于一个Person,但是Person不一定有PersonPhoto,这种关系是1:0..1,此种情况下Person是一定存在的,所以它是主从关系主的一方。
1: HasRequired(t => t.PhotoOf).WithOptional(t => t.Photo);
或
1: HasOptional(t => t.Photo).WithRequired(t => t.PhotoOf);
2.PersonPhoto必须属于一个Person,Person也必须有PersonPhoto,这种关系式1:1,此种情况下,两个都一定存在,要确定主从关系,需要使用WithRequiredPrincipal或WithRequiredDependent。
1: HasRequired(t => t.PhotoOf).WithRequiredDependent(t => t.Photo);
或
1: HasRequired(t => t.Photo).WithRequiredPrincipal(t => t.PhotoOf);
上述两种情况都是真实存在的,不真实存在的就不说了。
下面配置一对一关系贴出Demo:
//配置Person public class PersonConfiguration : EntityTypeConfiguration<Person> { public PersonConfiguration() { //主键 HasKey(t => t.PersonId); //并发检查 Property(t => t.SocialSecurityNumber).HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).IsConcurrencyToken(); //长度50 不为空 Property(t => t.FirstName).IsRequired().HasMaxLength(50); //长度50 不为空 Property(t => t.LastName).IsRequired().HasMaxLength(50); //并发检查 Property(t => t.RowVersion).IsRowVersion(); //HasRequired(t => t.Photo).WithRequiredPrincipal(t => t.PhotoOf); //HasOptional(t => t.Photo).WithRequired(t => t.PhotoOf); } } //配置PersonPhoto public class PersonPhotoConfiguration : EntityTypeConfiguration<PersonPhoto> { public PersonPhotoConfiguration() { //主键 HasKey(t => t.PersonId); //长度50 Property(t => t.Caption).HasMaxLength(50); //必须从属于Person HasRequired(t => t.PhotoOf).WithRequiredDependent(t => t.Photo); } } public class BreakAwayContext : DbContext { public DbSet<Person> People { get; set; } public DbSet<PersonPhoto> Photos { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new PersonConfiguration()); modelBuilder.Configurations.Add(new PersonPhotoConfiguration()); base.OnModelCreating(modelBuilder); } } public class Initializer : DropCreateDatabaseAlways<BreakAwayContext> { public Initializer() { } //创建数据库时 Seed数据 protected override void Seed(BreakAwayContext context) { context.People.Add(new Person() { FirstName = "E", LastName = "F", SocialSecurityNumber = 123456, Photo = new PersonPhoto() { Caption = "这是照片", Photo = new byte[] { } } }); context.SaveChanges(); }
二、一对多
下面是用到的类:
public class Blog { public Blog() { Posts = new List<Post>(); } public int Id { get; set; } public DateTime Creationdate { get; set; } public string ShortDescription { get; set; } public string Title { get; set; } public List<Post> Posts { get; set; } } public class Post { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public DateTime PostedDate { get; set; } public Nullable<int> BlogId { get; set; } public virtual Blog Blog { get; set; } public int PrimaryAuthorId { get; set; } public virtual Author PrimaryAuthor { get; set; } public Nullable<int> SecondaryAuthorId { get; set; } public virtual Author SecondaryAuthor { get; set; } } public class Author { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } //个人简历 public string Bio { get; set; } public List<Post> PrimaryAuthorFor { get; set; } public List<Post> SecondaryAuthorFor { get; set; } }
配置一对多关系常用的方法有:
HasOptional ,HasRequired ,HasMany
Has方法后面往往跟着With方法
WithOptional ,WithRequired ,WithMany
下面配置一对多的几种情况:
1.Post一定归属于一个Blog,这种关系是1:n。
1: HasMany(x => x.Posts).WithRequired(x =>x.Blog)
或
1: HasRequired(x => x.Blog).WithMany(x => x.Posts)
2.Post可以单独存在,不用归属于Blog,这种关系是0..1:n。
1: HasMany(x => x.Posts).WithOptional(x => x.Blog)
或
1: HasOptional(x => x.Blog).WithMany(x => x.Posts)
设置外键
外键的默认约定:
[Target Type Key Name], [Target Type Name] + [Target Type Key Name], or [Navigation
Property Name] + [Target Type Key Name]
本例中,匹配的是[Target Type Name] + [Target Type Key Name],目标类型是Blog,目标类型主键是Id,加起来就是BlogId。下面使用Fluent API显示设置外键:
1: HasMany(x => x.Posts).WithOptional(x => x.Blog).HasForeignKey(x => x.BlogId)
设置级联删除
1: HasMany(x => x.Posts).WithOptional(x => x.Blog).HasForeignKey(x => x.BlogId).WillCascadeOnDelete();
反转属性
在Post实体中,有两个属性:PrimaryAuthor和SecondaryAuthor,第一作者和第二作者。在Author中有两个集合属性,Code First默认不能确定哪个集合属性和Post中的导航属性相匹配。使用Fluent API配置反转属性,如下:
1: HasRequired(t => t.PrimaryAuthor).WithMany(t => t.PrimaryAuthorFor);
2: HasOptional(t => t.SecondaryAuthor).WithMany(t => t.SecondaryAuthorFor);
下面是配置一对多关系的Demo
public class BlogConfiguratioin : EntityTypeConfiguration<Blog> { public BlogConfiguratioin() { ToTable("Blogs"); HasKey(t => t.Id); Property(t => t.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(t => t.Title).IsRequired().HasMaxLength(250); Property(t => t.Creationdate).HasColumnName("CreationDate").IsRequired(); Property(t => t.ShortDescription).HasColumnType("Text").IsMaxLength().IsOptional().HasColumnName("Description"); //配置Blog和Post的一对多关系,Blog对Post是可选的,外键BlogId,并设置为级联删除 HasMany(t => t.Posts).WithOptional(t => t.Blog).HasForeignKey(t => t.BlogId).WillCascadeOnDelete(); } } public class PostConfiguration : EntityTypeConfiguration<Post> { public PostConfiguration() { ToTable("Posts"); HasKey(t => t.Id); Property(t => t.Id).HasColumnName("PostId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(t => t.Content).HasColumnName("Body").IsMaxLength(); Property(t => t.PostedDate).HasColumnName("PostedDate"); Property(t => t.Title).HasColumnName("Title").IsMaxLength(); //配置反转属性,集合属性PrimaryAuthorFor匹配PrimaryAuthor HasRequired(t => t.PrimaryAuthor).WithMany(t => t.PrimaryAuthorFor); //配置反转属性,集合属性SecondaryAuthorFor匹配SecondaryAuthor HasOptional(t => t.SecondaryAuthor).WithMany(t => t.SecondaryAuthorFor); } } public class AuthorConfiguration : EntityTypeConfiguration<Author> { public AuthorConfiguration() { ToTable("Authors"); HasKey(t => t.Id).Property(t => t.Id).HasColumnName("AuthorId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(t => t.Name).IsRequired().HasMaxLength(50); Property(t => t.Email).IsRequired().HasMaxLength(50); Property(t => t.Bio).HasMaxLength(1000); } } public class BreakAwayContext : DbContext { public DbSet<Blog> Blogs { get; set; } public DbSet<Post> Posts { get; set; } public DbSet<Author> Authors { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new BlogConfiguratioin()); modelBuilder.Configurations.Add(new PostConfiguration()); modelBuilder.Configurations.Add(new AuthorConfiguration()); base.OnModelCreating(modelBuilder); } } public class Initializer : DropCreateDatabaseAlways<BreakAwayContext> { public Initializer() { } protected override void Seed(BreakAwayContext context) { var primaryAuthor = new Author() { Name = "张三", Email = "zhangsan@126.com", Bio = "张三的简历" }; var secondaryAuthor = new Author() { Name = "李四", Email = "lisi@126.com", Bio = "李四的简历" }; var blog = new Blog() { Title = "EF", ShortDescription = "关于EF的博客", Creationdate = DateTime.Now }; blog.Posts.Add(new Post() { Title = "配置关系", PostedDate = DateTime.Now, Content = "这是Post的内容", PrimaryAuthor = primaryAuthor, SecondaryAuthor = secondaryAuthor }); context.Blogs.Add(blog); context.SaveChanges(); }
三、多对多
下面是配置多对多关系用到的类,跟一对多差不多,只不过Post和Author的关系变成多对多的了。
public class Post { public int Id { get; set; } public string Title { get; set; } public string Content { get; set; } public DateTime PostedDate { get; set; } public virtual List<Author> Authors { get; set; } } public class Author { public int Id { get; set; } public string Name { get; set; } public string Email { get; set; } //个人简历 public string Bio { get; set; } public virtual List<Post> Posts { get; set; } }
一篇文章有多个作者,一个作者著有多篇文章。
配置多对多关系使用HasMany和WithMany方法,可以使用Map配置生成关联表的名字。
下面是配置多对多关系的Demo:
public class PostConfiguration : EntityTypeConfiguration<Post> { public PostConfiguration() { ToTable("Posts"); HasKey(t => t.Id); Property(t => t.Id).HasColumnName("PostId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(t => t.Content).HasColumnName("Body").IsMaxLength(); Property(t => t.PostedDate).HasColumnName("PostedDate"); Property(t => t.Title).HasColumnName("Title").IsMaxLength(); //配置多对多关系 ToTable 配置生成的关联表名字 MapLeftKey默认表示调用HasMany的实体的主键 //本例中如果不使用MapLeftKey默认生成Post_Id HasMany(t => t.Authors).WithMany(t => t.Posts).Map(m => { m.ToTable("PostAuthor"); m.MapLeftKey("PostId"); m.MapRightKey("AuthorId"); }); } } public class AuthorConfiguration : EntityTypeConfiguration<Author> { public AuthorConfiguration() { ToTable("Authors"); HasKey(t => t.Id); Property(t => t.Id).HasColumnName("AuthorId").HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); Property(t => t.Bio).HasColumnType("Text").IsMaxLength(); Property(t => t.Email).HasMaxLength(100).IsRequired(); Property(t => t.Name).HasMaxLength(100).IsRequired(); } } public class TestContext : DbContext { public DbSet<Post> Posts { get; set; } public DbSet<Author> Authors { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { modelBuilder.Configurations.Add(new PostConfiguration()); modelBuilder.Configurations.Add(new AuthorConfiguration()); base.OnModelCreating(modelBuilder); } } public class Initializer : DropCreateDatabaseAlways<TestContext> { protected override void Seed(TestContext context) { var post = new Post() { Title = "Post1", Content = "Content1", PostedDate = DateTime.Now }; var author = new Author() { Name = "张三", Email = "zhangsan@126.com", Bio = "张三的简历" }; var author1 = new Author() { Name = "李四", Email = "lisi@126.com", Bio = "李四的简历" }; var author2 = new Author() { Name = "王五", Email = "wangwu@126.com", Bio = "王五的简历" }; post.Authors.Add(author); post.Authors.Add(author1); context.Posts.Add(post); post = new Post() { Title = "Post2", Content = "Content2", PostedDate = DateTime.Now }; post.Authors.Add(author); post.Authors.Add(author2); context.Posts.Add(post); context.SaveChanges(); } }
上一篇: 关于ThinkPHP通过AJAX返回JSON的方法
下一篇: 浏览器的渲染原理简介
推荐阅读
-
实例详解VSCode配置react开发环境的步骤
-
spring中aop的xml配置方法实例详解
-
quartz实现定时功能实例详解(servlet定时器配置方法)
-
Android编程操作嵌入式关系型SQLite数据库实例详解
-
IOS 图文混排(CoreText.framework)详解及实例
-
Spring boot中@Conditional和spring boot的自动配置实例详解
-
spring中aop的xml配置方法实例详解
-
Android读取properties配置文件的实例详解
-
Struts2拦截器Interceptor的原理与配置实例详解
-
Android编程操作嵌入式关系型SQLite数据库实例详解