EF 的安装
-
基础阶段用控制台项目。使用NuGet 安装EntityFramework。会自动在App.config中增加两个entityFramework 相关配置段;
-
在 web.config 中配置连接字符串
<add name="conn1" connectionString="Data Source=.;Initial Catalog=test1;UserID=sa;Password=123" providerName="System.Data.SqlClient" />
易错点:不能忘了写providerName="System.Data.SqlClient"增加两个entityFramework 相关配置段;
EF 简单DataAnnotations 实体配置
-
数据库中建表T_Perons,有Id(主键,自动增长)、Name、CreateDateTime字段。
-
创建Person类[Table(“T_Persons”)]因为类名和表名不一样,所以要使用Table标注
[Table("T_Persons")] public class Person { public long ID { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } }
因为EF约定主键字段名是Id,所以不用再特殊指定Id是主键,如果非要指定就指定[Key]。因为字段名字和属性名字一致,所以不用再特殊指定属性和字段名的对应关系,如果需要特殊指定,则要用[Column(“Name”)]
(*)必填字段标注[Required]、字段长度[MaxLength(5)]、可空字段用int?、如果字段在数据库有默认值,则要在属性上标注[DatabaseGenerated]注意实体类都要写成public,否则后面可能会有麻烦。
-
创建DbContext类(模型类、实体类)
public class MyDBContext: DbContext { //表示使用连接字符串中名字为conn1 的去连接数据库 public MyDBContext() : base("name=strcon") { } //通过对Persons 集合的操作就可以完成对T_Persons的操作 public DbSet<Person> Persons { get; set; } }
-
测试
protected void Button1_Click(object sender, EventArgs e) { MyDBContext context = new MyDBContext(); Person p=new Person(); p.Name =TextBox1.Text; p.CreateTime = DateTime.Now; context.Persons.Add(p); context.SaveChanges(); }
注意:MyDbContext 对象是否需要using有争议,不using也没事。每次用的时候new MyDbContext就行,不用共享同一个实例,共享反而会有问题。SaveChanges()才会把修改更新到数据库中。
EF的开发团队都说要using DbContext,很多人不using,只是想利用LazyLoad 而已,但是那样做是违反分层原则的。我的习惯还是using。
异常的处理:如果数据有错误可能在SaveChanges()的时候出现异常,一般仔细查看异常信息或者一直深入一层层的钻InnerException 就能发现错误信息。
举例:创建一个Person对象,不给Name、CreateDateTime赋值就保存。
EF 模型的两种配置方式
EF 中的模型类的配置有DataAnnotations、FluentAPI 两种。
上面这种在模型类上[Table(“T_Persons”)]、[Column(“Name”)]这种方式就叫DataAnnotations这种方式比较方便,但是耦合度太高,一般的类最好是POCO(Plain Old C# Object,没有继承什么特殊的父类,没有标注什么特殊的Attribute,没有定义什么特殊的方法,就是一堆普通的属性);不符合大项目开发的要求。微软推荐使用FluentAPI
的使用方式,因此后面主要用FluentAPI 的使用方式。
FluentAPI 配置T_Persons 的方式
-
数据库中建表T_Perons,有Id(主键,自动增长)、Name、CreateDateTime 字段。
-
创建 Person 类。模型类就是普通C#类
public class Person { public long ID { get; set; } public string Name { get; set; } public DateTime CreateTime { get; set; } }
-
创建一个 PersonConfig 类,放到ModelConfig 文件夹下(PersonConfig、EntityConfig这样的名字都不是必须的)
public class PersonConfig : EntityTypeConfiguration<Person> { public PersonConfig() { this.ToTable("T_Person"); } }
-
创建 DbContext 类
public class MyDBContext:DbContext { public MyDBContext() : base("name=strcon") { } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly()); } public DbSet<Person> Persons { get; set; } }
下面这句话:
modelBuilder.Configurations.AddFromAssembly(Assembly.GetExecutingAssembly());
代表从这句话所在的程序集加载所有的继承自EntityTypeConfiguration 为模型配置类。还有很多加载配置文件的做法(把配置写到OnModelCreating中或者把加载的代码写死到OnModelCreating 中),但是这种做法是最符合大项目规范的做法。
和以前唯一的不同就是:模型不需要标注Attribute;编写一个XXXConfig类配置映射关系;DbContext 中override OnModelCreating;
-
测试
protected void Button1_Click(object sender, EventArgs e) { MyDBContext context = new MyDBContext(); Person p = new Person(); p.Name = TextBox1.Text; p.CreateTime = DateTime.Now; context.Persons.Add(p); context.SaveChanges(); }
EF 的基本增删改查
获取DbSet除了可以ctx.Persons之外,还可以ctx.Set()。
-
增加,一个点:如果Id是自动增长的,创建的对象显然不用指定Id的值,并且在SaveChanges ()后会自动给对象的Id属性赋值为新增行的Id字段的值。
-
删除。先查询出来要删除的数据,然后Remove。这种方式问题最少,虽然性能略低,但是删除操作一般不频繁,不用考虑性能。后续在“状态管理”中会讲其他实现方法。
MyDBContext context = new MyDBContext(); if (e.CommandName=="BtnDelete") { int id = Convert.ToInt32(e.CommandArgument); var p = context.Persons.Where(per => per.ID == id).SingleOrDefault(); if (p!=null) { context.Persons.Remove(p); } int i= context.SaveChanges(); if (i>0) { Repeater1.DataSource = context.Persons.ToList(); Repeater1.DataBind(); } }
怎么批量删除,比如删除Id>3 的?查询出来一个个Remove。性能坑爹。如果操作不频繁或者数据量不大不用考虑性能,如果需要考虑性能就直接执行sql 语句
-
修改:先查询出来要修改的数据,然后修改,然后SaveChanges()
MyDbContext ctx = new MyDbContext(); var ps = ctx.Persons.Where(p => p.Id > 3); foreach(var p in ps) { p.CreateDateTime = p.CreateDateTime.AddDays(3); p.Name = "haha"; } ctx.SaveChanges();
性能问题?同上。
-
查。因为DbSet 实现了IQueryable 接口,而IQueryable 接口继承了IEnumerable
接口,所以可以使用所有的linq、lambda 操作。给表增加一个Age 字段,然后举例orderby、groupby、where
操作、分页等。一样一样的。 -
查询 order by 的一个细节
EF调用Skip之前必须调用OrderBy:如下调用var items = ctx.Persons.Skip(3).Take(5); 会报错“The method ‘OrderBy’ must be called before the method ‘Skip’.)”,要改成:var items = ctx.Persons.OrderBy(p=>p.CreateDateTime).Skip(3).Take(5);
这也是一个好习惯,因为以前就发生过(写原始sql):分页查询的时候没有指定排序规则,以为默认是按照Id 排序,其实有的时候不是,就造成数据混乱。写原始SQL 的时候也要注意一定要指定排序规则。