基本EF配置只要配置实体类和表、字段的对应关系、表间关联关系即可。如果利用EF的高级配置,可以达到更多效果:如果数据错误(比如字段不能为空、字符串超长等),会在EF层就会报错,而不会被提交给数据库服务器再报错;如果使用自动生成数据库,也能帮助EF生成更完美的数据库表。
基本步骤
在StudentConfig,实体对象的配置类中去设置指定的字符的其他配置
- 首先调用Property()方法获取目标字段
- Property()方法中使用的是Lambda表达式
- 获取到字段之后调用一系列EF函数设置字段的配置
字段的最大长度
HasMaxLength设定字段的最大长度
例如设置表中的Name的字段最大长度为50
this.Property(p=>p.Name).HasMaxLength(50);
如果插入一个Person对象,Name属性的值非常长,保存的时候就会报DbEntityValidationException异常,这个异常的Message中看不到详细的报错消息,要看EntityValidationErrors属性的值。
字段是否可空
this.Property(p => p.Pwd).IsRequired();//不为空
this.Property(p => p.Pwd).IsOptional();//可为空(没用的鸡肋!)
EF默认规则是“主键属性不允许为空,引用类型允许为空,可空的值类型long?等允许为空,值类型不允许为空。”基于“尽量少配置”的原则:如果属性是值类型并且允许为null,就声明成long?等,否则声明成long等;如果属性属性值是引用类型,只有不允许为空的时候设置IsRequired()。
其他一般不用设置的
- 主键:this.HasKey(p => p.pId);
- 某个字段不参与映射数据库:this.Ignore(p => p.Name1);
- this.Property(p => p.Name).IsFixedLength(); 是否对应固定长度
- this.Property(p => p.Name).IsUnicode(false) 对应的数据库类型是varchar类型,而不是nvarchar
- this.Property(p => p.Id).HasColumnName(“Id”); Id列对应数据库中名字为Id的字段
- this.Property(p=>p.Id).HasDatabaseGeneratedOption(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity) 指定字段是自动增长类型。
流动起来
因为ToTable()、Property()、IsRequired()等方法的还是配置对象本身,因此可以实现类似于StringBuilder的链式编程,这就是“Fluent”一词的含义;
一对多关系映射
EF最有魅力的地方在于对于多表间关系的映射,可以简化工作。 复习一下表间关系:
- 一对多(多对一):一个班级对应着多个学生,一个学生对着一个班级。一方是另外一方的唯一。在多端有一个指向一端的外键。
举例:
班级表:T_Classes(Id,Name)
学生表T_Students(Id,Name,Age,ClassId) - 多对多:一个老师对应多个学生,一个学生对于多个老师。任何一方都不是对方的唯一。 需要一个中间关系表。
具体:
学生表T_Students(Id,Name,Age,ClassId)
老师表 T_Teachers(Id,Name,PhoneNum)
关系表T_StudentsTeachers(Id,StudentId,TeacherId)
和关系映射相关的方法
- 基本套路this.Has(p=>p.A).With***() 当前这个表和A 属性的表的关系是Has 定义, With 定义的是A 对应的表和这个表的关系。Optional/Required/Many
- HasOptional() 有一个可选的(可以为空的)
- HasRequired() 有一个必须的(不能为空的)
- HasMany() 有很多的
- WithOptional() 可选的
- WithRequired() 必须的
- WithMany() 很多的
配置一对多关系
- 先按照正常的单表配置把Student、ClassRoom 配置起来,Students 的ClassId 字段就对应Student类的ClassId 属性。
- 给Student类增加一个ClassRoom类型、名字为CRoom(不一定非叫这个,但是习惯是:外键名去掉Id)的属性,要声明成virtual(后面讲原因)
- 然后就可以实现各种对象间操作了:
数据插入也变得简单了,不用再考虑“先保存Class,生成Id,再保存Student”了。这样就是纯正的“面向对象模型”,ClassId 属性可以删掉。 - 如果ClassId 字段可空怎么办?直接把ClassId 属性设置为long?
- 还可以在Class中配置一个public virtual ICollection Students { get; set; } = new List(); 属性。最好给这个属性初始化一个对象。注意是virtual。这样就可以获得所有指向了当前对象的Stuent集合,也就是这个班级的所有学生。我个人不喜欢这个属性,业界的大佬也是建议“尽量不要设计双向关系”,因为可以通过Class clz = ctx.Classes.First(); var students =ctx.Students.Where(s => s.ClassId == clz.Id);来查询获取到,思路更清晰。