asp.net mvc框架之使用EF一对多关系的设计及使用
在开发过程中,学生和教室的关系(一个学生只能对应一个教室,一个教室对应多个学生),部门的领导和部门的职员等,几乎无处不在。
以学生和教室的关系:在学校中,一个学生必须对应一个班级,如果一个好学生的话,他不仅对应一个班级,而且学校还会再开一个学霸班在休息日或者放学后进行上课。所以分析:学生必须对应一个班级(一对多关系),但是一个学生可能会对应一个学霸班(有的学生在学霸班,有的学生不能上学霸班)。
1.创建数据库中的表:
Class3表
Id --编号(int)
Name --班级名称(nvarchar(50))
Student3表
Id --编号(int)
Name --学生名称(nvarchar(50))
ClassId --班级编号(int,外键关联Class3的Id)
SYClassId --特殊班级编号(int,外键关联Class3的Id)
2.安装EntityFramework框架 -- Install-Package EntityFramework
3.创建实体类(Student3,Class3)
public class Class3 { public int Id { get; set; } public string Name { get; set; } }
public class Student3 { public int Id { get; set; } public string Name { get; set; } public int ClassId { get; set; } //int?表示对应字段的表中的列可以为空 public int? SYClassId { get; set; } //对应外键,要声明一个关于外键关联的主键表的类型,并且命名方式:外键:名Id(或名_Id);对应的外键的主键表类型是外键名减去Id public virtual Class3 Class { get; set; } public virtual Class3 SYClass { get; set; } }
4.创建Student3和Class3的配置类
public class Class3Config:EntityTypeConfiguration<Class3> { public Class3Config() { this.ToTable("T_Classes3"); } }
public class Students3Config:EntityTypeConfiguration<Student3> { public Students3Config() { this.ToTable("T_Students3"); this.HasRequired(s => s.Class).WithMany().HasForeignKey(c => c.ClassId); this.HasOptional(s => s.SYClass).WithMany().HasForeignKey(c => c.SYClassId); } }
其中定义的规则就是:Has...(s=>s.外键的主键表类型).with...().ForeignKey(外键);
判断规则:从左向右,例如:学生必须对应一个班级,一个班级一定对应多个学生,所以就是选择HasRequired->WithMany;学生可能对应一个特殊班级,一个特殊班级一定对应多个学生,所以选择:HasOptional->WithMany
5.创建继承DbContext的子类Test3DbContext,添加无参数的构造函数,重写OnModelCreating方法
public class Test3DbContext:DbContext { public Test3DbContext():base("name=conn1") { } public DbSet<Class3> Classes3 { get; set; } public DbSet<Student3> Students3 { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); } }
几点重要的注意点:
(1)base()里面要写数据库字符串连接名称
(2)有多个个实体类,创建多少个DbSet<T>类型的属性
6.实现一对多关系,添加到数据库中
Test3DbContext tdc3 = new Test3DbContext(); Class3 cls1 = new Class3(); cls1.Name = "初一1班"; tdc3.Classes3.Add(cls1); Class3 cls2 = new Class3(); cls2.Name = "初一2班"; tdc3.Classes3.Add(cls2); Class3 cls3 = new Class3(); cls3.Name = "学霸先进班"; tdc3.Classes3.Add(cls3); Student3 s1 = new Student3(); s1.Name = "张三"; s1.Class = cls1; tdc3.Students3.Add(s1); Student3 s2 = new Student3(); s2.Name = "李四"; s2.Class = cls2; s2.SYClass = cls3; tdc3.Students3.Add(s2); tdc3.SaveChanges(); Console.WriteLine("ok"); Console.ReadKey();
注意点:
1.在Student3实体类中添加Class3数据类型的属性,属性名不一定非要叫这个,只是习惯外键去掉Id,但是必须要添加virtual
2.对于可空字段:引用类型是默认可空的,值类型字段需要long? 、int?
3.对于一对多关系,可以在Class3中配置public virtual ICollection<Student3> Students{get;set;} = new List<Students>();属性。最好给这个属性初始化一个对象,而这样初始化一个对象只能在C#6.0中使用,如果6.0以下的话需要在构造函数中进行初始化。建议不要这样使用,因为会造成耦合度高。
使用:
Test3DbContext tdc3 = new Test3DbContext();
Class3 c1 = new Class3{Name="高一1班"};
tdc3.Classes3.Add(c1);
Student3 s1 = new Student3{Name="张三"};
Student3 s2 = new Student3{Name="李四"};
c1.Students.Add(s1);//相当于将s1对象的学生加入到了c1对象的班级中
4.一对多的配置:(在每个实体类的配置类中进行配置)
(1)关系映射基本套路:this.Has***(p=>p.AAA).With***().HasForeignKey(s=>s.AAAId); 当前表和AAA属性的表的关系是:当前表->AAA对应的数据类型的表的对应关系,With***AAA对应当前表的关系。
HasOptional() 有一个可选的(可以为空)
HasRequired() 必须有一个(不能为空)
HasMany() 有很多
同理:
WithOption()
WithRequired()
WithMany()
5.Fluent API的配置
(1)HasMaxLength设定字段的最大长度
this.Property(p=>p.Name).HasMaxLength(50)设置Name属性长度为50
当如果使用Name字段插入一个超出50长度的字符串,会报DbEntityValidationException异常,这个异常的Message中看不到详细的错误消息,要看EntityValidationException属性的值,具体如下:
。。。
。。。
try
{
tdc3.SaveChanges();
}
catch(DbEntityValidationException ex)
{
StringBuilder sb = new StringBuilder();
foreach(var ve in ex.EntityValidationErrors.SelectMany(eve=>eve.ValidationErrors))
{
sb.AppendLine(ve.PropertyName+":"+ve.ErrorMessage);
}
Console.WriteLine(sb);
}
(2)字段是否可空(针对于引用类型)
this.Property(p=>p.Name).IsRequired()属性不能为空
this.Property(p=>p.Name).IsOptional() 属性可以为空(鸡肋,原因是引用类型默认为空)
(3)主键如果不是默认的Id,那么就需要配置
主键:this.HasKey(p=>p.pId);
(4)this.Property(p=>p.Name).IsUnicode(false);对应的数据类型是varchar类型。IsUnicode(false)表示varchar类型,IsUnicode(true)表示nvarchar类型
(5)this.Property(p.p.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity) 指定字段是自动增长类型