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) 指定字段是自动增长类型

 

posted @ 2019-07-09 14:34  black娃  阅读(839)  评论(0编辑  收藏  举报