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实现),用了延迟加载就要多次的执行数据库操作,提高了数据库服务器的压力
因此:如果关联的属性几乎都要读到,那么就不要用延迟加载;如果关联的属性只有较小的概率则可以启动延迟记载;
6、不延迟加载,怎样一次加载?
又想方便(必须是virtual)又想效率高!用EF永远都要把导航属性设置为virtual。
使用Include()方法:
(1)var s= ctx.Students.Include("Class").First(); //Include("Class")的意思是直接加载student的Class属性的数据。注意只有关联的对象属性才可以用Include,普通字段不可以;
(2) C#6.0 语法糖,可以使用nameof语法解决这个问题:
ctx.Students.Include(nameof(Student.Class)).First();
(3) //using System.Data.Entity; 推荐这种做法
ctx.Students.Include(e=>e.Class).First();
如果有多个属性需要一次性加载,也可以写多个Include:
(4)
using (TestDbContext ctx = new TestDbContext())
{
foreach (var s in ctx.Students)
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
} //报错,多个DataReader一起执行了
三种解决方法:
在字符串上加上:MultipleActiveResultSets=true; 但是不支持其他数据库
执行一下Tolist() ,因为Tolist() 就遍历然后生成list:
using (TestDbContext ctx = new TestDbContext())
{
foreach (var s in ctx.Students.ToList())
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
}
推荐做法:Include预先加载:
using (TestDbContext ctx = new TestDbContext())
{
foreach (var s in ctx.Students.Include(e=>e.Class))
{
Console.WriteLine(s.Name);
Console.WriteLine(s.Class.Name);
}
}