深入理解ADO.NET Entity Framework(02)
目录:
- ORM概念
- 数据迁移
- EF状态管理
- 加载
- 缓存
- 事务
- 封装EF的DAL层
Ado.net Entity Framework 是Microsoft推出的ORM框架。
1.ORM
对象映射关系对数据库进行操作,解决面向对象与关系数据库不匹配的现象。
- CSDL :概念层 O (对象)
- SSDL :存储层 R(配置文件 标记对象对应数据库的表)
- MSL :对应层 M (映射)
EF包括三种模式:DBFirst、CodeFist、ModelFirst 。
(1)数据库到对象模型
- 在项目中添加"ADO.NET实体数据模型",选择"来自数据库的EF设计器",按照提示配置数据库相关信息。选择相关的数据库表。
- 打开.emdx文件,edmx中包含SSDL、CSDL和C-S mapping三种类型的节点定义,分别对应EDM中的存储层、概念层、对应层。这也是EF实现ORM的关键配置文件。
- 导航属性 -- 外键列 (一对一 (obj) 一对多(Icollection<obj>))
- DBContext -- uisng system.data.Entity -- 数据库上下文
- 操作数据
//查询 using (MySchoolEntities entities = new MySchoolEntities()){ //按名称查询年级编号 Grade grade = entities.Grade.SingleOrDefault(g => g.GradeName == "S2"); if (grade!=null) Console.WriteLine("S2年级的编号为{0}",grade.GradeId); //用where()方法查询符合条件的数据 IQueryable<Student> query = entities.Student.Where(s => s.GradeId == grade.GradeId); Console.WriteLine("学号\t姓名"); foreach (var stu in query) {Console.WriteLine("{0}\t{1}",stu.StudentNo,stu.StudentName);}} //添加数据 using (MySchool1Entities entities = new MySchool1Entities()) {Student stu=new Student(){StudentNo="S10001",StudentName="王翱翔",GradeId=2,Sex="男"}; entities.Student.Add(stu); //向实体集添加数据 if (entities.SaveChanges()>0) //将添加的数据保存到数据库中 Console.WriteLine("添加数据成功!");} //修改数据 using (MySchool1Entities entities = new MySchool1Entities()){ //先查询要修改的数据 Student stu = entities.Student.FirstOrDefault(s => s.StudentName == "王翱翔"); if (stu!=null) {stu.LoginPwd = "123456"; //修改对象属性值} if (entities.SaveChanges()>0) //将修改的对象保存到数据库中 {Console.WriteLine("修改数据成功!");}} //删除数据 using (MySchool1Entities entities = new MySchool1Entities()){ //先查询要删除的数据 Student stu = entities.Student.FirstOrDefault(s => s.StudentName == "王翱翔"); if (stu!=null) {entities.Student.Remove(stu);} //从数据集中移除数据if (entities.SaveChanges()>0) {Console.WriteLine("删除数据成功!");}}
注意:需要额外做的两件事是创建数据上下文对象和调用其SaveChange()方法实现数据的增、删、改。
(2)从代码到数据库
- 安装Entity Framework package包、创建实体类、创建DbContext类、创建MySchoolContext类
<configuration> <connectionStrings> <add name="MySchool" providerName="System.Data.SqlClient" connectionString="Data Source=.;Initial Catalog=Test;Persist Security Info=True;User ID=sa;Password=sa"/> </connectionStrings> </configuration> //创建MySchoolContext类 sing System.Data.Entity; public class MySchoolContext:DbContext { //构造函数,会读取配置文件中的连接字符串 public MySchoolContext():base("MySchool") //.config文件数据库连接字符串名称 {} //对象集合 public DbSet<Grade> Grade { get; set; } public DbSet<Student> Student { get; set; } //重新父类中的方法(当模型初始化后被调用) protected override void OnModelCreating(DbModelBuilder modelBuilder) { //指定单数形式的表名(否则数据库的表名会是复数形式) //需要命名空间:using System.Data.Entity.ModelConfiguration.Conventions; modelBuilder.Conventions.Remove<PluralizingTableNameConvention>(); } }
- EF中的默认约定
①关于表和字段名的约定。映射列和属性命名一致
②关于主键的约定。将命名为 Id 或 [类名]Id 的属性视为类的键,如果是int类型同时会设为标识列。
③字符串属性的约定。字符串约定为映射到不限长度的非空列中,默认数据类型为nvarchar(max),且允许为空。
④布尔值的约定。Code Frirst将bool属性映射为 bit 类型,且不允许为空。
⑤关系。1对1、1对多、多对多
- 配置覆盖约定
①使用Data Annotation特性
public class Student { [Key] //主键声明 public string studentKey { get; set; } [Required] //非空声明 public string stuName { get; set; } [MaxLength(10)] //最大长度 public string stuTxt1 { get; set; } [MaxLength(10), MinLength(2)] //最大长度和最小长度 public string stuTxt2 { get; set; } [ConcurrencyCheck] //并发检查 public string stuTxt3 { get; set; } public virtual Address stuAddress { get; set; } } [ Table("myAddress")] //设置类映射的数据库表名 public class Address { [ForeignKey("stu")] //设置外键(对应下面声明的 stu) //这里符合 类名+id(忽略大小写)的规则,所以自动生成主键 public string AddressId { get; set; } [Column("myAddressName")] //设置映射数据库中表的列名 public string AddressName { get; set; } [Column("myAddressName2", Order = 1, TypeName = "varchar")] //设置映射数据库中表的列名、顺序、类型 public string AddrssName2 { get; set; } [NotMapped]//不映射数据 public string addressNum { get; set; } public virtual Student stu { get; set; } }
②使用Fluent API
可以将一个类映射成多个数据库表,还可以将配置写成多个文件,方便控制。优先级:Fluent API > data annotations > default conventions.
常见配置:
❶获取表对应的配置根: var stu =modelBuilder.Entity<XXX>();
❷设置主键:HasKey<string>(s => s.studentKey);
❸获取属性:stu.Property(p => p.stuName)
❹设置可空或非空:IsRequired和IsOptional
❺设置最大值:HasMaxLength
❻修改属性名→修改属性的次序→修改属性对应的数据库类型:HasColumnName→HasColumnOrder→HasColumnType
❼修改表名:ToTable
可以建立多个Fluent API的配置文件,然后通过modelBuilder.Configurations.Add(new XXX());添加到一起。
public class Student { //主键声明 public string studentKey { get; set; } //非空声明 public string stuName { get; set; } //最大长度 public string stuTxt1 { get; set; } //最大长度和最小长度 public string stuTxt2 { get; set; } //并发检查 public string stuTxt3 { get; set; } } public class Address { //既是主键、又是外键 public string AddressId { get; set; } //设置映射数据库中表的列名 public string AddressName { get; set; } //设置映射数据库中表的列名、顺序、类型 public string AddrssName2 { get; set; } //不映射数据 public string addressNum { get; set; } } /// <summary> /// Game实体,与其它两个没有什么直接关系,单纯的为了演示, Fluent API的配置,可以根据实体进行拆分 /// 文件来配置,方便管理 /// </summary> public class Game { public int GameId { get; set; } public string GameName { get; set; } } /// <summary> /// Game实体的配置文件 /// </summary> public class GameConfiguration : EntityTypeConfiguration<Game> { public GameConfiguration() { this.HasKey(p => p.GameId); this.Property(p => p.GameName).HasMaxLength(10).IsRequired(); } } public class dbContext : DbContext { public dbContext(): base("name=MySchool") { } public DbSet<Student> Student { get; set; } public DbSet<Address> Address { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { //所有的FluentAPI均在方法中进行重写 //一. 属性层次上的设置 var stu = modelBuilder.Entity<Student>(); var stuAddress = modelBuilder.Entity<StudentAddress>(); //1. 设置主键 stu.HasKey<string>(s => s.studentKey); stuAddress.HasKey<string>(s => s.AddressId); //2. 设置非空 (扩展:IsOptional 设置可空) stu.Property(p => p.stuName).IsRequired( ); //3. 设置最大值(不能设置最小值) stu.Property(p => p.stuTxt1).HasMaxLength(10); //4. 修改列的名称、排序、类型 stuAddress.Property(p => p.stuAddrssName2).HasColumnName("myAddress2").HasColumnOrder(1).HasColumnType("varchar"); //5.修改表名 stu.Map<Student>(c => { c.ToTable("MyStudent"); }); //6.将一个实体映射成多张表,并分别给其命名 //stuAddress.Map<StudentAddress>(c => //{ // c.Properties(p => new // { // p.AddressId, // p.AddressName // }); // c.ToTable("MyStuAddress1"); //}).Map<StudentAddress5>(c => //{ // c.Properties(p => new // { // p.stuAddressId, // p.stuAddrssName2 // }); // c.ToTable("MyStuAddress2"); //}); //三. 将Game实体的配置添加进来 modelBuilder.Configurations.Add(new GameConfiguration()); base.OnModelCreating(modelBuilder); } }
(3)EF CodeFirst 数据库初始化策略和数据迁移
①数据库初始化策略
四种策略:
- CreateDatabaseIfNotExists:EF的默认策略,一旦model发生变化,抛异常,提示数据迁移
- DropCreateDatabaseIfModelChanges:一旦model发生变化,删除数据库重新生成
- DropCreateDatabaseAlways:数据库每次都重新生成
- 自定义初始化(继承上面的三种策略中任何一种,然后追加自己的业务)
也可以关闭Database.SetInitializer<dbContext6>(null);关闭后改变实体类,不会报错,不会丢失数据,但也无法映射改变数据库结构。
public class MySchoolEntities : DbContext { public MySchoolEntities (): base("name=MySchool") { //在这里可以改变生成数据库的初始化策略 //1. CreateDatabaseIfNotExists (EF的默认策略,数据库不存在,生成数据库;一旦model发生变化,抛异常,提示走数据迁移) //Database.SetInitializer<MySchoolEntities>(new CreateDatabaseIfNotExists<MySchoolEntities >()); //2. DropCreateDatabaseIfModelChanges (一旦model发生变化,删除数据库重新生成) //Database.SetInitializer<MySchoolEntities >(new DropCreateDatabaseIfModelChanges<MySchoolEntities >()); //3.DropCreateDatabaseAlways (数据库每次都重新生成) //Database.SetInitializer<MySchoolEntities >(new DropCreateDatabaseAlways<MySchoolEntities >()); //4. 自定义初始化(继承上面的三种策略中任何一种,然后追加自己的业务) //Database.SetInitializer<MySchoolEntities >(new MySpecialIntializer()); //5. 禁用数据库策略(不会报错,不会丢失数据,但是改变不了数据库的结构了) //Database.SetInitializer<MySchoolEntities >(null); } public DbSet<Animal> Animal { get; set; } public DbSet<AnimalKind> AnimalKind { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } /// <summary> /// 自定义一个初始化策略,每次初始化时追加10条数据 /// </summary> public class MySpecialIntializer : DropCreateDatabaseAlways<MySchoolEntities > { public override void InitializeDatabase(MySchoolEntities context) { base.InitializeDatabase(context); } //重写seed,追加初始数据 protected override void Seed(MySchoolEntities context) { for (int i = 0; i < 10; i++) { context.Animal.Add(new Animal() { id = "animal" + i, animalName = "mru" + i, animalKind = "mruru" + i, }); } context.SaveChanges(); base.Seed(context); } } }
②数据迁移
解决修改表结构,数据丢失的问题。两种方式:
- 自动迁移(不安全)
//(1)新建Configuration.cs类,在其构造函数中进行相关配置。 internal sealed class Configuration : DbMigrationsConfiguration<MySchoolEntities > { public Configuration() { AutomaticMigrationsEnabled = true; //(2)启用自动迁移 AutomaticMigrationDataLossAllowed = true; //(3)更改数据库中结构(增加、删除列、修改列、改变列的属性、增加、删除、修改表),需要显示开启。}} public class MySchoolEntities : DbContext { public MySchoolEntities (): base("name=MySchool") { //数据库迁移配置 //数据库迁移的初始化方式 Database.SetInitializer(new MigrateDatabaseToLatestVersion<MySchoolEntities , Configuration>("MySchoolEntities")); } public DbSet<Animal> Animal { get; set; } public DbSet<AnimalKind> AnimalKind { get; set; } protected override void OnModelCreating(DbModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); } }
- 手动迁移
(1)通过“程序包管理器控制台”的enable-migrations命令启动数据迁移。该命令只需执行一次
作用:在项目根目录下创建了一个Migrations文件夹,在Migrations文件夹下新建一个Configuration.cs文件。
(2)执行添加“add-migration InitialCreate”命令
作用:产生一个<timestamp>_InitialCreate.cs的文件,文件中的两个方法(Up和Down)用来完成数据的更改
(3)执行更新“update-database”命令
作用:完成数据库的更新
- 迁移回退
PM> update-database -TargetMigration:SchoolDB-v1
1.1 ORM性能问题
- 复杂的对象管理机制:执行数据库会全程跟踪,降低性能
- 高度封装执行机制:编写的表达式都要解析成SQL语句。
- 低效的SQL语句:映射机制转将对象操作换为SQL语句,效率低。
1.2 EF的状态管理
在程序中实现数据的增、删、改操作,EF会监控这些状态的变化,在执行SaveChange()方法时,会根据对象状态的变化执行相应的操作。
using (MySchoolContext db = new MySchoolContext()) { Grade grade = new Grade() { GradeName = "Y3" }; //输出当前对象状态 Console.WriteLine(db.Entry(grade).State); //通过Entry()方法获取模型状态 db.Grade.Add(grade); Console.WriteLine(db.Entry(grade).State); db.SaveChanges(); Console.WriteLine(db.Entry(grade).State); }
方法或属性 |
说明 |
CurrentValues |
获取由此对象表示的跟踪实体的当前属性值 |
OriginalValues |
获取由此对象表示的跟踪实体的原始属性值 |
State |
获取或设置实体的状态 |
Reload() |
从数据库重新加载对象的值 |
其中,State属性是一个EntityState枚举类型,其取值如下:
执行过程: --Detached 游离、Added 添加、Unchanged 未改变、Modified 已修改、Deleted 已删除、Detached 游离
Detached | 表示对象存在,但没有被跟踪 |
Unchanged | 表示对象尚未经过修改 |
Added | 表示对象为新对象,并且已添加到对象上下文 |
Deleted | 对象已从对象上下文中删除 |
Modified | 表示对象的一个标量属性已更改 |
1.3查询不进行跟踪
- AsNoTracking()方法
using (MySchoolContext db = new MySchoolContext()) { var result = db.Student.AsNoTracking().FirstOrDefault(); Console.WriteLine(db.Entry(result).State); } |
- 设置Configuration.AutoDetectChangesEnabled 属性为false
using (MySchoolContext db = new MySchoolContext()) { //禁用自动跟踪变化 db.Configuration.AutoDetectChangesEnabled = false; for (int i = 0; i < 5000; i++) { var stu = new Student() { StudentName = "alex", GradeId = 1, Age = 20 }; db.Student.Add(stu); } db.SaveChanges(); } |
在使用EF修改或删除数据时,必须先查询对象,然后再对其进行修改或删除。然而现实开发中很多情况都是通过主键删除一条数据。我们可以通过实体的状态特性来进行优化。
using (MySchool1Entities entities = new MySchool1Entities()) { //创建替身对象 var stu = new Student { StudentNo = "10001" }; //给实体附加到上下文中 entities.Student.Attach(stu); //删除 entities.Student.Remove(stu); entities.SaveChanges(); } |
代码中的Attach()方法可以将EntityState.Unchangee状态的对象附加到上下文中。
2.加载
- 延迟加载
每次调用时再去查询,两个条件:①Poco类是Public且不能为Sealed。 ②导航属性需要标记为Vritual。
- 贪懒加载
一次性将数据读取出来,从缓存中读取,不用在查询数据库,两个条件:①先关闭延迟加载。 ②查询主表的同时通过Include把从表数据也查询出来。
- 显示加载
--步骤:①单个实体用:Reference ②集合用:Collection ③最后需要Load一下
注意:默认用延迟,多次读数据库用贪婪。
//延迟加载 using (dbContext1 db = new dbContext1()) { Console.WriteLine("---------------- 01-延迟加载 ---------------"); //EF默认就是延迟加载,默认下面的语句就是true,关闭则false db.Configuration.LazyLoadingEnabled = true; var list = db.Student.ToList(); //此处加载的数据,没有对从表进行任何查询操作 foreach (var stu in list) { Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName); //下面调用导航属性(一对一的关系) 每次调用时,都要去查询数据库 var stuAddress = stu.StudentAddress; Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName); } } //贪懒加载 using (dbContext1 db = new dbContext1()) { Console.WriteLine("------------------- 03-立即加载 ------------------"); //1.关闭延迟加载 db.Configuration.LazyLoadingEnabled = false; //2. 获取主表数据的同时,通过Include将从表中的数据也全部加载出来 var list = db.Student.Include("StudentAddress").ToList(); foreach (var stu in list) { Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName); //这里获取从表中的数据,均是从缓存中获取,无需查询数据库 var stuAddress = stu.StudentAddress; Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName); } } //显示加载 using (dbContext1 db = new dbContext1()) { Console.WriteLine("----------------- 04-显式加载 ------------------"); //1.关闭延迟加载 db.Configuration.LazyLoadingEnabled = false; //2.此处加载的数据,不含从表中的数据 var list = db.Student.ToList(); foreach (var stu in list) { Console.WriteLine("学生编号:{0},学生姓名:{1}", stu.studentId, stu.studentName); //3.下面的这句话,可以开启重新查询一次数据库 //3.1 单个属性的情况用Refercence db.Entry<Student>(stu).Reference(c => c.StudentAddress).Load(); //3.2 集合的情况用Collection //db.Entry<Student>(stu).Collection(c => c.StudentAddress).Load(); //下面调用导航属性(一对一的关系) 每次调用时,都要去查询数据库 var stuAddress = stu.StudentAddress; Console.WriteLine("地址编号:{0},地址名称:{1}", stuAddress.studentAddressId, stu.studentName); } }
3.缓存
- EF的dbset<T>的Local属性提供缓存
- DbSet<T>提供了 Find()方法,用于通过主键查找实体速度比First方法快的多,并且如果相应的实体已经被DbContext缓存,EF会在缓存中直接返回对应的实体,不执行数据库访问。
4.事务
- SaveChanges
- DbContextTransaction
- TransactionScope
using (DbContext db = new CodeFirstModel()) { //增加 TestInfor t1 = new TestInfor() { id = Guid.NewGuid().ToString("N"), txt1 = "txt1", txt2 = "txt2" }; db.Set<TestInfor>().Add(t1); //删除 TestInfor t2 = db.Set<TestInfor>().Where(u => u.id == "1").FirstOrDefault(); if (t2 != null) { db.Set<TestInfor>().Remove(t2); } //修改 TestInfor t3 = db.Set<TestInfor>().Where(u => u.id == "3").FirstOrDefault(); t3.txt2 = "我是李马茹23"; //SaveChanges事务提交 int n = db.SaveChanges(); //一次性统一或回滚 Console.WriteLine("数据作用条数:" + n); }
using (DbContext db = new CodeFirstModel()) { DbContextTransaction trans = null; try { //开启事务 trans = db.Database.BeginTransaction(); //多个SaveChanges //增加 string sql1 = @"insert into TestInfor values(@id,@txt1,@txt2)"; SqlParameter[] pars1 ={ new SqlParameter("@id",Guid.NewGuid().ToString("N")), new SqlParameter("@txt1","txt11"), new SqlParameter("@txt2","txt22") }; db.Database.ExecuteSqlCommand(sql1, pars1); //删除 string sql2 = @"delete from TestInfor where id=@id"; SqlParameter[] pars2 ={ new SqlParameter("@id","22") }; db.Database.ExecuteSqlCommand(sql2, pars2); //修改 string sql3 = @"update TestInfor set txt1=@txt1 where id=@id"; SqlParameter[] pars3 ={ new SqlParameter("@id","3"), new SqlParameter("@txt1","二狗子") }; db.Database.ExecuteSqlCommand(sql3, pars3); //提交事务 trans.Commit(); Console.WriteLine("事务成功了"); } catch (Exception ex) { Console.WriteLine(ex.Message); trans.Rollback(); //回滚 } finally { //也可以把该事务写到using块中,让其自己托管,就不需要手动释放了 trans.Dispose(); } }
using (DbContext db = new CodeFirstModel()) { //自动脱管,不需要手动释放 多数据库连接 using (DbContextTransaction trans = db.Database.BeginTransaction()) { try { TestInfor t1 = new TestInfor() { id = Guid.NewGuid().ToString("N"), txt1 = "111111111", txt2 = "222222222222" }; db.Entry(t1).State = EntityState.Added; db.SaveChanges(); TestInfor t2 = new TestInfor() { id = Guid.NewGuid().ToString("N") + "123", txt1 = "111111111", txt2 = "222222222222" }; db.Entry(t2).State = EntityState.Added; db.SaveChanges(); trans.Commit(); } catch (Exception) { trans.Rollback(); } } }
5.从实体框架回归SQL
EF在DbContext类的Database属性里提供了ExecuteSqlCommand()和SqlQuery()两个方法,用来直接访问数据库。
- ExecuteSqlCommand()
- SqlQuery()
using (MySchool1Entities db = new MySchool1Entities()) { //执行update语句 string sql = "update grade set gradeName=@gradeName where gradeId=@gradeId"; SqlParameter[] ps = { new SqlParameter("@gradeName","第二学年"), new SqlParameter("@gradeId",3) }; int result=db.Database.ExecuteSqlCommand(sql, ps); //返回影响行数 if (result>0) { Console.WriteLine("数据更新完成!"); } //执行查询语句 sql = "select * from from student where studentNo=@stuNo"; ps = new SqlParameter[] { new SqlParameter("@stuNo", "S1001234") }; var stu = db.Database.SqlQuery<Student>(sql, ps); //返回集合 Console.WriteLine(stu.ToList()[0]); }
封装EF的DAL层
public class BaseDAL<T> where T:class 2 { 3 private DbContext db 4 { 5 get 6 { 7 DbContext dbContext = CallContext.GetData("dbContext") as DbContext; 8 if (dbContext == null) 9 { 10 dbContext = new MySchoolContext(); 11 CallContext.SetData("dbContext", dbContext); 12 } 13 return dbContext; 14 } 15 } 16 17 /// <summary> 18 /// 执行增加,删除,修改操作(或调用存储过程) 19 /// </summary> 20 /// <param name="sql"></param> 21 /// <param name="pars"></param> 22 /// <returns></returns> 23 public int ExecuteSql(string sql, params SqlParameter[] pars) 24 { 25 return db.Database.ExecuteSqlCommand(sql, pars); 26 } 27 28 /// <summary> 29 /// 执行查询操作 30 /// </summary> 31 /// <typeparam name="T"></typeparam> 32 /// <param name="sql"></param> 33 /// <param name="pars"></param> 34 /// <returns></returns> 35 public List<T> ExecuteQuery(string sql, params SqlParameter[] pars) 36 { 37 return db.Database.SqlQuery<T>(sql, pars).ToList(); 38 } 39 40 /// <summary> 41 /// 添加 42 /// </summary> 43 /// <param name="model"></param> 44 /// <returns></returns> 45 public int Add(T model) 46 { 47 db.Set<T>().Add(model); 48 return db.SaveChanges(); 49 } 50 51 /// <summary> 52 /// 删除(适用于先查询后删除的单个实体) 53 /// </summary> 54 /// <param name="model">需要删除的实体</param> 55 /// <returns></returns> 56 public int Del(T model) 57 { 58 db.Set<T>().Attach(model); 59 db.Set<T>().Remove(model); 60 return db.SaveChanges(); 61 } 62 63 /// <summary> 64 /// 根据条件删除(支持批量删除) 65 /// </summary> 66 /// <param name="delWhere">传入Lambda表达式(生成表达式目录树)</param> 67 /// <returns></returns> 68 public int DelBy(Expression<Func<T, bool>> delWhere) 69 { 70 var listDels = db.Set<T>().Where(delWhere); 71 foreach(var model in listDels) 72 { 73 db.Set<T>().Attach(model); 74 db.Set<T>().Remove(model); 75 } 76 return db.SaveChanges(); 77 } 78 79 /// <summary> 80 /// 修改 81 /// </summary> 82 /// <param name="model">修改后的实体</param> 83 /// <returns></returns> 84 public int Modify(T model) 85 { 86 db.Entry(model).State = EntityState.Modified; 87 return db.SaveChanges(); 88 } 89 90 /// <summary> 91 /// 批量修改 92 /// </summary> 93 /// <param name="model">要修改实体中 修改后的属性 </param> 94 /// <param name="whereLambda">查询实体的条件</param> 95 /// <param name="proNames">lambda的形式表示要修改的实体属性名</param> 96 /// <returns></returns> 97 public int ModifyBy(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) 98 { 99 List<T> listModifes = db.Set<T>().Where(whereLambda).ToList(); 100 Type t = typeof(T); 101 List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); 102 Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>(); 103 proInfos.ForEach(p => 104 { 105 if (proNames.Contains(p.Name)) 106 { 107 dicPros.Add(p.Name, p); 108 } 109 }); 110 foreach (string proName in proNames) 111 { 112 if (dicPros.ContainsKey(proName)) 113 { 114 PropertyInfo proInfo = dicPros[proName]; 115 object newValue = proInfo.GetValue(model, null); 116 foreach (T m in listModifes) 117 { 118 proInfo.SetValue(m, newValue, null); 119 } 120 } 121 } 122 return db.SaveChanges(); 123 } 124 125 /// <summary> 126 /// 根据条件查询 127 /// </summary> 128 /// <param name="whereLambda">查询条件(lambda表达式的形式生成表达式目录树)</param> 129 /// <returns></returns> 130 public IQueryable<T> GetListBy(Expression<Func<T, bool>> whereLambda) 131 { 132 return db.Set<T>().Where(whereLambda); 133 } 134 /// <summary> 135 /// 根据条件排序和查询 136 /// </summary> 137 /// <typeparam name="Tkey">排序字段类型</typeparam> 138 /// <param name="whereLambda">查询条件</param> 139 /// <param name="orderLambda">排序条件</param> 140 /// <param name="isAsc">升序or降序</param> 141 /// <returns></returns> 142 public IQueryable<T> GetListBy<Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 143 { 144 if (isAsc) 145 { 146 return db.Set<T>().Where(whereLambda).OrderBy(orderLambda); 147 } 148 else 149 { 150 return db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda); 151 } 152 } 153 /// <summary> 154 /// 分页查询 155 /// </summary> 156 /// <typeparam name="Tkey">排序字段类型</typeparam> 157 /// <param name="pageIndex">页码</param> 158 /// <param name="pageSize">页容量</param> 159 /// <param name="whereLambda">查询条件</param> 160 /// <param name="orderLambda">排序条件</param> 161 /// <param name="isAsc">升序or降序</param> 162 /// <returns></returns> 163 public IQueryable<T> GetPageList<Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 164 { 165 166 IQueryable<T> list = null; 167 if (isAsc) 168 { 169 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda) 170 .Skip((pageIndex - 1) * pageSize).Take(pageSize); 171 } 172 else 173 { 174 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda) 175 .Skip((pageIndex - 1) * pageSize).Take(pageSize); 176 } 177 return list; 178 } 179 /// <summary> 180 /// 分页查询输出总行数 181 /// </summary> 182 /// <typeparam name="Tkey">排序字段类型</typeparam> 183 /// <param name="pageIndex">页码</param> 184 /// <param name="pageSize">页容量</param> 185 /// <param name="whereLambda">查询条件</param> 186 /// <param name="orderLambda">排序条件</param> 187 /// <param name="isAsc">升序or降序</param> 188 /// <returns></returns> 189 public IQueryable<T> GetPageList<Tkey>(int pageIndex, int pageSize, out int rowCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 190 { 191 IQueryable<T> list = null; 192 rowCount = db.Set<T>().Where(whereLambda).Count(); 193 if (isAsc) 194 { 195 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda) 196 .Skip((pageIndex - 1) * pageSize).Take(pageSize); 197 } 198 else 199 { 200 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda) 201 .Skip((pageIndex - 1) * pageSize).Take(pageSize); 202 } 203 return list; 204 } 205 206 207 }
- DbContextTransaction