您的位置:首页 > 其它

【配置关系】—Entity Framework实例详解

2012-11-12 21:53 281 查看
实体间的关系,简单来说无非就是一对一、一对多、多对多,根据方向性来说又分为双向和单向。CodeFirst在实体关系上有以下约定:

1.两个实体,如果一个实体包含一个引用属性,另一个实体包含一个集合属性,CodeFirst默认约定它们为一对多关系。
2.两个实体,如果只有一个实体包含一个导航属性或一个集合属性,CodeFirst也默认约定它们是一对多关系。
3.两个实体分别包含一个集合属性,CodeFirst默认约定它们为多对多关系。
4.两个实体分别包含一个引用属性,CodeFirst默认约定它们为一对一关系。
5.在一对一关系情况下,需要提供给CodeFirst额外的信息,以确定它们的主从关系。
6.在实体中定义一个外键属性,CodeFirst使用属性是否为空来确定关系是必须还是可选。

一、一对一

在CodeFirst中,一对一关系总是需要配置,因为两个实体都包含有一个引用属性,无法确定它们的主从关系。

配置一对一关系常用的方法:

HasRequired,HasOptional,WithOptional,WithRequiredPrincipal,WithRequiredDependent

下面是用到的类:

publicclassPerson

[code]{
publicintPersonId{get;set;}

publicintSocialSecurityNumber{get;set;}

publicstringFirstName{get;set;}

publicstringLastName{get;set;}

publicbyte[]RowVersion{get;set;}

publicPersonPhotoPhoto{get;set;}

}

publicclassPersonPhoto

{

publicintPersonId{get;set;}

publicbyte[]Photo{get;set;}

publicstringCaption{get;set;}

publicPersonPhotoOf{get;set;}

}

[/code]

因为Photo是具体人的,所以PersonPhoto使用PersonId作为主键。

下面是一对一关系配置的几种情况:

1.PersonPhoto必须属于一个Person,但是Person不一定有PersonPhoto,这种关系是1:0..1,此种情况下Person是一定存在的,所以它是主从关系主的一方。


HasRequired(t=>t.PhotoOf).WithOptional(t=>t.Photo);






HasOptional(t=>t.Photo).WithRequired(t=>t.PhotoOf);



2.PersonPhoto必须属于一个Person,Person也必须有PersonPhoto,这种关系式1:1,此种情况下,两个都一定存在,要确定主从关系,需要使用WithRequiredPrincipal或WithRequiredDependent。


HasRequired(t=>t.PhotoOf).WithRequiredDependent(t=>t.Photo);






HasRequired(t=>t.Photo).WithRequiredPrincipal(t=>t.PhotoOf);



上述两种情况都是真实存在的,不真实存在的就不说了。

下面配置一对一关系贴出Demo:

publicclassPerson


{


publicintPersonId{get;set;}


publicintSocialSecurityNumber{get;set;}


publicstringFirstName{get;set;}


publicstringLastName{get;set;}


publicbyte[]RowVersion{get;set;}


publicPersonPhotoPhoto{get;set;}


}




publicclassPersonPhoto


{


publicintPersonId{get;set;}


publicbyte[]Photo{get;set;}


publicstringCaption{get;set;}


publicPersonPhotoOf{get;set;}


}




//配置Person


publicclassPersonConfiguration:EntityTypeConfiguration<Person>


{


publicPersonConfiguration()


{


//主键


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


publicclassPersonPhotoConfiguration:EntityTypeConfiguration<PersonPhoto>


{


publicPersonPhotoConfiguration()


{


//主键


HasKey(t=>t.PersonId);


//长度50


Property(t=>t.Caption).HasMaxLength(50);


//必须从属于Person


HasRequired(t=>t.PhotoOf).WithRequiredDependent(t=>t.Photo);


}


}




publicclassBreakAwayContext:DbContext


{


publicDbSet<Person>People{get;set;}


publicDbSet<PersonPhoto>Photos{get;set;}




protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)


{


modelBuilder.Configurations.Add(newPersonConfiguration());


modelBuilder.Configurations.Add(newPersonPhotoConfiguration());


base.OnModelCreating(modelBuilder);


}


}




publicclassInitializer:DropCreateDatabaseAlways<BreakAwayContext>


{


publicInitializer()


{


}




//创建数据库时Seed数据


protectedoverridevoidSeed(BreakAwayContextcontext)


{


context.People.Add(newPerson()


{


FirstName="E",


LastName="F",


SocialSecurityNumber=123456,


Photo=newPersonPhoto()


{


Caption="这是照片",


Photo=newbyte[]{}


}


});


context.SaveChanges();


}


}


测试程序

[TestClass]


publicclassUnitTest1


{


[TestMethod]


publicvoidShouldReturnAPersonWithPhoto()


{


//Arrange


varinit=newInitializer();


Personperson;


using(varcontext=newBreakAwayContext())


{


init.InitializeDatabase(context);


//Act


person=context.People.Include(t=>t.Photo).FirstOrDefault();


}


//Assert


Assert.IsNotNull(person);


Assert.IsNotNull(person.Photo);


}


}


测试结果:





二、一对多

下面是用到的类:


publicclassBlog

[code]{
publicBlog()

{

Posts=newList<Post>();

}


publicintId{get;set;}

publicDateTimeCreationdate{get;set;}

publicstringShortDescription{get;set;}

publicstringTitle{get;set;}

publicList<Post>Posts{get;set;}

}


publicclassPost

{

publicintId{get;set;}

publicstringTitle{get;set;}

publicstringContent{get;set;}

publicDateTimePostedDate{get;set;}


publicNullable<int>BlogId{get;set;}

publicvirtualBlogBlog{get;set;}


publicintPrimaryAuthorId{get;set;}

publicvirtualAuthorPrimaryAuthor{get;set;}

publicNullable<int>SecondaryAuthorId{get;set;}

publicvirtualAuthorSecondaryAuthor{get;set;}

}


publicclassAuthor

{

publicintId{get;set;}

publicstringName{get;set;}

publicstringEmail{get;set;}

//个人简历

publicstringBio{get;set;}


publicList<Post>PrimaryAuthorFor{get;set;}

publicList<Post>SecondaryAuthorFor{get;set;}

}

[/code]

配置一对多关系常用的方法有:

HasOptional,HasRequired,HasMany

Has方法后面往往跟着With方法

WithOptional,WithRequired,WithMany

下面配置一对多的几种情况:

1.Post一定归属于一个Blog,这种关系是1:n。


HasMany(x=>x.Posts).WithRequired(x=>x.Blog)






HasRequired(x=>x.Blog).WithMany(x=>x.Posts)



2.Post可以单独存在,不用归属于Blog,这种关系是0..1:n。


HasMany(x=>x.Posts).WithOptional(x=>x.Blog)






HasOptional(x=>x.Blog).WithMany(x=>x.Posts)



设置外键

外键的默认约定:

[TargetTypeKeyName],[TargetTypeName]+[TargetTypeKeyName],or[Navigation
PropertyName]+[TargetTypeKeyName]

本例中,匹配的是[TargetTypeName]+[TargetTypeKeyName],目标类型是Blog,目标类型主键是Id,加起来就是BlogId。下面使用FluentAPI显示设置外键:


HasMany(x=>x.Posts).WithOptional(x=>x.Blog).HasForeignKey(x=>x.BlogId)



设置级联删除


HasMany(x=>x.Posts).WithOptional(x=>x.Blog).HasForeignKey(x=>x.BlogId).WillCascadeOnDelete();



反转属性

在Post实体中,有两个属性:PrimaryAuthor和SecondaryAuthor,第一作者和第二作者。在Author中有两个集合属性,CodeFirst默认不能确定哪个集合属性和Post中的导航属性相匹配。使用FluentAPI配置反转属性,如下:


HasRequired(t=>t.PrimaryAuthor).WithMany(t=>t.PrimaryAuthorFor);

[code]HasOptional(t=>t.SecondaryAuthor).WithMany(t=>t.SecondaryAuthorFor);
[/code]

下面是配置一对多关系的Demo:

publicclassBlog


{


publicBlog()


{


Posts=newList<Post>();


}




publicintId{get;set;}


publicDateTimeCreationdate{get;set;}


publicstringShortDescription{get;set;}


publicstringTitle{get;set;}


publicList<Post>Posts{get;set;}


}




publicclassPost


{


publicintId{get;set;}


publicstringTitle{get;set;}


publicstringContent{get;set;}


publicDateTimePostedDate{get;set;}




//Post可以不归属到Blog独立存在,注意这里的外键属性要设置为可空的


publicNullable<int>BlogId{get;set;}


publicvirtualBlogBlog{get;set;}




publicintPrimaryAuthorId{get;set;}


publicvirtualAuthorPrimaryAuthor{get;set;}


publicNullable<int>SecondaryAuthorId{get;set;}


publicvirtualAuthorSecondaryAuthor{get;set;}


}




publicclassAuthor


{


publicintId{get;set;}


publicstringName{get;set;}


publicstringEmail{get;set;}


//个人简历


publicstringBio{get;set;}




publicList<Post>PrimaryAuthorFor{get;set;}


publicList<Post>SecondaryAuthorFor{get;set;}


}




publicclassBlogConfiguratioin:EntityTypeConfiguration<Blog>


{


publicBlogConfiguratioin()


{


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();


}


}




publicclassPostConfiguration:EntityTypeConfiguration<Post>


{


publicPostConfiguration()


{


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);


}


}




publicclassAuthorConfiguration:EntityTypeConfiguration<Author>


{


publicAuthorConfiguration()


{


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);


}


}




publicclassBreakAwayContext:DbContext


{


publicDbSet<Blog>Blogs{get;set;}


publicDbSet<Post>Posts{get;set;}


publicDbSet<Author>Authors{get;set;}




protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)


{


modelBuilder.Configurations.Add(newBlogConfiguratioin());


modelBuilder.Configurations.Add(newPostConfiguration());


modelBuilder.Configurations.Add(newAuthorConfiguration());


base.OnModelCreating(modelBuilder);


}


}




publicclassInitializer:DropCreateDatabaseAlways<BreakAwayContext>


{


publicInitializer()


{


}




protectedoverridevoidSeed(BreakAwayContextcontext)


{


varprimaryAuthor=newAuthor()


{


Name="张三",


Email="zhangsan@126.com",


Bio="张三的简历"


};


varsecondaryAuthor=newAuthor()


{


Name="李四",


Email="lisi@126.com",


Bio="李四的简历"


};


varblog=newBlog()


{


Title="EF",


ShortDescription="关于EF的博客",


Creationdate=DateTime.Now


};


blog.Posts.Add(newPost()


{


Title="配置关系",


PostedDate=DateTime.Now,


Content="这是Post的内容",


PrimaryAuthor=primaryAuthor,


SecondaryAuthor=secondaryAuthor


});


context.Blogs.Add(blog);


context.SaveChanges();


}


}


测试程序:

[TestClass]


publicclassOneToManyTest


{


[TestMethod]


publicvoidShouldReturnBlogWithPosts()


{


//Arrage


Database.SetInitializer(newInitializer());


varcontext=newBreakAwayContext();


//Act


varblog=context.Blogs.Include(t=>t.Posts).FirstOrDefault();


//Assert


Assert.IsNotNull(blog);


Assert.IsNotNull(blog.Posts);


Assert.IsNotNull(blog.Posts.FirstOrDefault().PrimaryAuthor);


}


}


测试结果:





三、多对多

下面是配置多对多关系用到的类,跟一对多差不多,只不过Post和Author的关系变成多对多的了。


publicclassPost

[code]{
publicintId{get;set;}

publicstringTitle{get;set;}

publicstringContent{get;set;}

publicDateTimePostedDate{get;set;}


publicvirtualList<Author>Authors{get;set;}

}


publicclassAuthor

{

publicintId{get;set;}

publicstringName{get;set;}

publicstringEmail{get;set;}

//个人简历

publicstringBio{get;set;}


publicvirtualList<Post>Posts{get;set;}

}

[/code]

一篇文章有多个作者,一个作者著有多篇文章。

配置多对多关系使用HasMany和WithMany方法,可以使用Map配置生成关联表的名字。

下面是配置多对多关系的Demo:

publicclassPost


{


publicPost()


{


Authors=newList<Author>();


}




publicintId{get;set;}


publicstringTitle{get;set;}


publicstringContent{get;set;}


publicDateTimePostedDate{get;set;}




publicvirtualList<Author>Authors{get;set;}


}




publicclassAuthor


{


publicAuthor()


{


Posts=newList<Post>();


}




publicintId{get;set;}


publicstringName{get;set;}


publicstringEmail{get;set;}


//个人简历


publicstringBio{get;set;}




publicvirtualList<Post>Posts{get;set;}


}




publicclassPostConfiguration:EntityTypeConfiguration<Post>


{


publicPostConfiguration()


{


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");


});


}


}




publicclassAuthorConfiguration:EntityTypeConfiguration<Author>


{


publicAuthorConfiguration()


{


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();


}


}




publicclassTestContext:DbContext


{


publicDbSet<Post>Posts{get;set;}


publicDbSet<Author>Authors{get;set;}




protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)


{


modelBuilder.Configurations.Add(newPostConfiguration());


modelBuilder.Configurations.Add(newAuthorConfiguration());


base.OnModelCreating(modelBuilder);


}


}




publicclassInitializer:DropCreateDatabaseAlways<TestContext>


{


protectedoverridevoidSeed(TestContextcontext)


{


varpost=newPost()


{


Title="Post1",


Content="Content1",


PostedDate=DateTime.Now


};


varauthor=newAuthor()


{


Name="张三",


Email="zhangsan@126.com",


Bio="张三的简历"


};


varauthor1=newAuthor()


{


Name="李四",


Email="lisi@126.com",


Bio="李四的简历"


};


varauthor2=newAuthor()


{


Name="王五",


Email="wangwu@126.com",


Bio="王五的简历"


};


post.Authors.Add(author);


post.Authors.Add(author1);


context.Posts.Add(post);


post=newPost()


{


Title="Post2",


Content="Content2",


PostedDate=DateTime.Now


};


post.Authors.Add(author);


post.Authors.Add(author2);


context.Posts.Add(post);


context.SaveChanges();


}


}


测试程序:

[TestClass]


publicclassManyToManyTest


{


[TestMethod]


publicvoidShouldReturnPostWithAuthors()


{


//Arrage


varinit=newInitializer();


varcontext=newManyToMany.TestContext();


init.InitializeDatabase(context);


//Act


varpost=context.Posts.Include(t=>t.Authors).FirstOrDefault();


//Assert


Assert.IsNotNull(post);


Assert.AreEqual(2,post.Authors.Count);


Assert.AreEqual("李四",post.Authors[1].Name);


}


}


测试结果:





现在关联表中只有两个字段,如下图所示:





如果再加个字段,比如DateAdd,这就需要给关联表定义一个实体。


publicclassPostAuthor

[code]{
publicintPostId{get;set;}

publicintAuthorId{get;set;}


publicPostPost{get;set;}

publicAuthorAuthor{get;set;}


publicDateTimeDateAdd{get;set;}

}

[/code]

另外需要在Post和Author实体中加入一个集合属性:


publicvirtualList<PostAuthor>PostAuthors{get;set;}



另外还需要配置PostAuthor实体,具体代码如下面的Demo所示:

publicclassPost


{


publicPost()


{


PostAuthors=newList<PostAuthor>();


}




publicintId{get;set;}


publicstringTitle{get;set;}


publicstringContent{get;set;}


publicDateTimePostedDate{get;set;}




//publicvirtualList<Author>Authors{get;set;}


publicvirtualList<PostAuthor>PostAuthors{get;set;}


}




publicclassAuthor


{


publicAuthor()


{


PostAuthors=newList<PostAuthor>();


}




publicintId{get;set;}


publicstringName{get;set;}


publicstringEmail{get;set;}


//个人简历


publicstringBio{get;set;}




//publicvirtualList<Post>Posts{get;set;}


publicvirtualList<PostAuthor>PostAuthors{get;set;}


}




//关联表的实体


publicclassPostAuthor


{


publicintPostId{get;set;}


publicintAuthorId{get;set;}




publicPostPost{get;set;}


publicAuthorAuthor{get;set;}




publicDateTime?DateAdd{get;set;}


}




publicclassPostConfiguration:EntityTypeConfiguration<Post>


{


publicPostConfiguration()


{


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();


}


}




publicclassAuthorConfiguration:EntityTypeConfiguration<Author>


{


publicAuthorConfiguration()


{


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();


}


}




//配置关联表实体


publicclassPostAuthorConfiguration:EntityTypeConfiguration<PostAuthor>


{


publicPostAuthorConfiguration()


{


ToTable("PostAuthors");


//配置组合主键


HasKey(t=>new{t.PostId,t.AuthorId});


Property(t=>t.PostId).HasColumnOrder(0);


Property(t=>t.AuthorId).HasColumnOrder(1);


//这里是配置一对多关系


HasRequired(t=>t.Post).WithMany(t=>t.PostAuthors).HasForeignKey(t=>t.PostId);


HasRequired(t=>t.Author).WithMany(t=>t.PostAuthors).HasForeignKey(t=>t.AuthorId);


}


}




publicclassTestContext:DbContext


{


publicDbSet<Post>Posts{get;set;}


publicDbSet<Author>Authors{get;set;}


publicDbSet<PostAuthor>PostAuthors{get;set;}




protectedoverridevoidOnModelCreating(DbModelBuildermodelBuilder)


{


modelBuilder.Configurations.Add(newPostConfiguration());


modelBuilder.Configurations.Add(newAuthorConfiguration());


modelBuilder.Configurations.Add(newPostAuthorConfiguration());


base.OnModelCreating(modelBuilder);


}


}




publicclassInitializer:DropCreateDatabaseAlways<TestContext>


{


protectedoverridevoidSeed(TestContextcontext)


{


varpost=newPost()


{


Title="Post1",


Content="Content1",


PostedDate=DateTime.Now


};


post=context.Posts.Add(post);


varauthor=newAuthor()


{


Name="张三",


Email="zhangsan@126.com",


Bio="张三的简历"


};


varauthor1=newAuthor()


{


Name="李四",


Email="lisi@126.com",


Bio="李四的简历"


};


author=context.Authors.Add(author);


author1=context.Authors.Add(author1);


context.SaveChanges();


PostAuthorpa1=newPostAuthor()


{


PostId=post.Id,


AuthorId=author.Id,


DateAdd=DateTime.Now


};


PostAuthorpa2=newPostAuthor()


{


PostId=post.Id,


AuthorId=author1.Id,


DateAdd=DateTime.Now


};


context.PostAuthors.Add(pa1);


context.PostAuthors.Add(pa2);


context.SaveChanges();


}


}


测试程序:

[TestMethod]


publicvoidShouldReturnAuthorsWithDateAdd()


{


//Arrage


varinit=newInitializer();


varcontext=newManyToMany.TestContext();


init.InitializeDatabase(context);


//Act


varpost=context.Posts.Include(t=>t.PostAuthors).FirstOrDefault();


//Assert


Assert.IsNotNull(post);


Assert.AreEqual(2,post.PostAuthors.Count);


Assert.IsNotNull(post.PostAuthors[0].DateAdd);


}


测试结果:





生成的关联表如下图所示:





四、结束语

点击查看《EntityFramework实例详解》系列的其他文章。

如果遇到问题,可以访问EntityFramework社区,网址是www.ef-community.com或www.ef-community.cn。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: