FluentAPI深入
Posted 鬼草道人
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了FluentAPI深入相关的知识,希望对你有一定的参考价值。
1. HasMaxLenght 设定字段得最大长度:
static void Main(string[] args) { using (TestDbContext ctx = new TestDbContext()) { Person p = new Person { Name = "chenwwwwwwwwwwwwwwwwwwwwww", Age=12, CreateDateTime=DateTime.Now }; ctx.Persons.Add(p); try { ctx.SaveChanges(); } catch (DbEntityValidationException ex) { foreach (var e in ex.EntityValidationErrors) { foreach (var error in e.ValidationErrors) { Console.WriteLine(error.ErrorMessage); } } } Console.ReadKey(); } }
依赖于数据库得“字段长度、是否为空”等约束是在数据提交到数据库服务器得时候才会检查‘EF得配置,则是由EF来检查得,如果检查出错根本不会被提交给服务器。
2. (有用)字段是否为空:
public PersonConfig() { this.ToTable("T_Persons"); this.Property(p=>p.Name).HasMaxLength(20); this.Property(p => p.Name).IsRequired();//属性不能为空 this.Property(p => p.Name).IsOptional();//属性可以为空 }
默认规则是“主键属性不允许为空,引用类型允许为空”
public PersonConfig() { this.ToTable("T_Persons"); this.HasKey(p => p.Id); //主键 this.Property(p => p.Name).IsFixedLength(); //是否对应固定得长度 this.Property(p => p.Name).IsUnicode(false);//对应得数据库类型是varchar类型,而不是nvarchar this.Property(p => p.Name).HasColumnName("Names"); //Name对应数据库中得字段名是Names this.Ignore(p => p.Name);//某个字段不参与映射数据库 }
3、一对多
和关系映射相关得方法:
(1) 基本套路 this.Has****(p=>p.A).With****() 当前这个表和A属性表的关系是Has定义, With定义的是A对应的表和这个表的关系。
(2) HasOptional() 有一个是可选的(可以为空的); HasRequlred() 有一个是必须的(不能为空的); HasMany() 有很多的;
(3)WithOptional() 可选的; WithRequired() 必须的 ; WithMany() 很多的;
实例一:
public class Student { public long Id { get; set; } public string Name { get; set; } public int Age { get; set; } public long ClassId { get; set; } public virtual Class Class { get; set; } } public class Class { public long Id { get; set; } public string Name { get; set; } public int Count { get; set; } }
public class ClassConfig:EntityTypeConfiguration<Class> { public ClassConfig() { ToTable("T_Classes"); } } public class StudentConfig:EntityTypeConfiguration<Student> { public StudentConfig() { ToTable("T_Students"); } }
Class c1 = new Class() { Name = "三年一班", }; Student s1 = new Student() { Name = "chen", Age = 12, Class = c1 }; ctx.Students.Add(s1); ctx.SaveChanges();
实例二:
var s = ctx.Classes.First(); //双向设计,容易搞混 /* foreach (var item in s.Students) { Console.WriteLine(item.Name); } */ //数据库化思维,推荐用这种用法 foreach (var item in ctx.Students.Where(p => p.ClassId == s.Id)) { Console.WriteLine(item.Name); }
public class Class { public long Id { get; set; } public string Name { get; set; } public int Count { get; set; } public virtual ICollection<Student> Students { get; set; } = new List<Student>(); //这里就可以获得所有指向了当前对象的Student集合,不推荐这种双向设计关系 }
4、 多对多
public class Teacher { public int Id { get; set; } public string Name { get; set; } public virtual ICollection<Student> Students { get; set; } = new List<Student>(); } public class Student { public long Id { get; set; } public string Name { get; set; } public int Age { get; set; } public long? Class_Id { get; set; } public virtual Class Class { get; set; } public virtual ICollection<Teacher> Teachers { get; set; } = new List<Teacher> (); }
public class StudentConfig:EntityTypeConfiguration<Student> { public StudentConfig() { ToTable("T_Students"); this.HasRequired(s => s.Class).WithMany().HasForeignKey(e => e.Class_Id); } } public class TeacherConfig:EntityTypeConfiguration<Teacher> { public TeacherConfig() { ToTable("T_Teachers"); this.HasMany(e => e.Students).WithMany(e=>e.Teachers).Map(e => e.ToTable("T_TeacherStudentRelations").MapLeftKey("teacherId").MapRightKey("studentId")); } }
static void Main(string[] args) { using (TestDbContext ctx = new TestDbContext()) { Teacher t1 = new Teacher() { Name = "语文老师" }; Teacher t2 = new Teacher() { Name = "数学老师" }; Student s1 = new Student() { Name = "小李", Age = 23 }; Student s2 = new Student() { Name = "小王", Age = 33 }; Student s3 = new Student() { Name = "小章", Age = 33 }; t1.Students.Add(s1); t1.Students.Add(s2); t2.Students.Add(s2); t2.Students.Add(s3); ctx.Teachers.Add(t1); ctx.Teachers.Add(t2); ctx.SaveChanges(); Console.ReadKey(); } }
总结: (1)一对多中不建议配置一端的集合属性,因此配置的时候不用给WithMany()参数,如果配置了集合属性,则必须给WithMany参数;多对多关系必须要给WithMany参数
(2) 多对多移除关系:
static void Main(string[] args) { using (TestDbContext ctx = new TestDbContext()) { var t=ctx.Teachers.Single(p=>p.Name=="数学老师"); var s = ctx.Students.Single(p => p.Name == "小章"); t.Students.Remove(s); ctx.SaveChanges(); Console.ReadKey(); } }
(3)如果数据库创建好了再修改模型或者配置,运行就会报错,那么就要手动删除数据库或者
Database.SetInitializer(new DropCreateDatabaseIfModelChanges<TestDbContext>());
(4)做项目时建议初期先把主要的类使用EF自动生成表,然后干掉Migration表,然后
Database.SetInitializer<TestDbContext>(null); //禁止DbMigration使用,手动修改实体类和数据库
5、延迟加载:
延迟加载(lazy load) ,只有用到关联的对象的数据,才会再去指向select查询,注意延迟加载只在关联对象属性上,普通属性没有这个东西;
注意启用延迟加载需要配置如下的两个属性(默认是true,因此不需要配置,只要别手动设置为false即可)
context.Configuration..ProxyCreationEnable=true; context.Configuration..LazyLoadingEnable=true;
分析延迟加载的原理:打印一下拿到的对象的GetType(),再打印一下GetType().BaseType;我们发现拿到的对象其实是Student子类的对象(如果结果不一致,说明类不是Public,没有关联的virtua属性l)
延迟加载的优点: 用到的时候才加载,没用到的时候不加载,避免了一次性加载所有的数据,提高了加载速度。缺点: 如果不用延迟加载,就可以一次数据库查询所有数据(join实现),用了延迟加载就要多次的执行数据库操作,提高了数据库服务器的压力
因此:如果关联的属性几乎都要读到,那么就不要用延迟加载;如果关联的属性只有较小的概率则可以启动延迟记载;
以上是关于FluentAPI深入的主要内容,如果未能解决你的问题,请参考以下文章
在不修改实体类(注释)或数据上下文(使用 fluentapi)的情况下禁用标识(自动递增)
深入理解DOM节点类型第四篇——文档片段节点DocumentFragment