1、增加对象
1 2 3 4 5 6 7 8 9 10 |
DbEntity db = new DbEntity(); //创建对象实体,注意,这里需要对所有属性进行赋值(除了自动增长主键外),如果不赋值,则会数据库中会被设置为NULL(注意是否可空) var user = new User { Name = "bomo", Age = 21, Gender = "male" }; db.User.Add(user); db.SaveChanges(); |
2、删除对象,删除只需要对象的主键
1 2 3 4 5 6 7 8 9 10 11 |
DbEntity db = new DbEntity(); //删除只需要主键,这里删除主键为5的行 var user = new User {Id = 5}; //将实体附加到对象管理器中 db.User.Attach(user); //方法一: db.User.Remove(user); //方法二:把当前实体的状态改为删除 //db.Entry(user).State = EntityState.Deleted; db.SaveChanges(); |
3、修改对象 方法一:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
DbEntity db = new DbEntity(); //修改需要对主键赋值,注意:这里需要对所有字段赋值,没有赋值的字段会用NULL更新到数据库 var user = new User { Id = 5, Name = "bomo", Age = 21, Gender = "male" }; //将实体附加到对象管理器中 db.User.Attach(user); //把当前实体的状态改为Modified db.Entry(user).State = EntityState.Modified; db.SaveChanges(); |
方法二:方法一中每次都需要对所有字段进行修改,效率低,而且麻烦,下面介绍修改部分字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
DbEntity db = new DbEntity(); //修改需要对主键赋值,注意:这里需要对所有字段赋值,没有赋值的字段会用NULL更新到数据库 var user = new User { Id = 5, Name = "bomo", Age = 21 }; //将实体附加到对象管理器中 db.User.Attach(user); //获取到user的状态实体,可以修改其状态 var setEntry = ((IObjectContextAdapter) db).ObjectContext.ObjectStateManager.GetObjectStateEntry(user); //只修改实体的Name属性和Age属性 setEntry.SetModifiedProperty("Name"); setEntry.SetModifiedProperty("Age"); db.SaveChanges(); |
4、使用事务:使用事务很简单,只要把需要的操作放在 TransactionScope 中,最后提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
DbEntity db = new DbEntity(); using (var scope = new TransactionScope()) { //执行多个操作 var user1 = new User { Name = "bomo", Age = 21, Gender = "male" }; db.User.Add(user1); db.SaveChanges(); var user2 = new User { Name = "toroto", Age = 20, Gender = "female" }; db.User.Add(user2); db.SaveChanges(); //提交事务 scope.Complete(); } |
5、查询:查询通过LinQ查询
1 2 3 4 |
DbEntity db = new DbEntity(); //选择部分字段 var user = db.User.Where(u => u.Name == "bomo").Select(u => new {Id = u.Id, Name = u.Name, Age = u.Age}).FirstOrDefault(); //只有调用了FirstOrDefault, First, Single, ToList, ToArray等函数才会执行对数据库的查询 |
from:http://www.cnblogs.com/bomo/p/3331602.html
View Details在Entity Framework 中使用SaveChanges()是很频繁的,单次修改或删除数据后调用SaveChanges()返回影响记录数。 要使用批量修改或者批量删除数据,就需要SaveChanges(false)+AcceptAllChanges()方法了。 SaveChanges(false) 只是通知EF需要对数据库执行的操作,在内存中是属于挂起状态,在必要的时候是可以撤销的,比如AcceptAllChange()提交为真正成功,EF将撤销SaveChanges(false)的操作。 而在处理分布式事务操作的时候,就有必要使用TransactionScope 来处理了,很多时候我们会这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save and discard changes context1.SaveChanges(); //Save and discard changes context2.SaveChanges(); //if we get here things are looking good. scope.Complete(); } |
但是这样写是有风险的,假 如context1.SaveChanges()成功了,context2.SaveChanges()却是有问题的,我们在scope.Complete()提交事务的时候就会终止,而Context1已经成功执行了 这可能不一定符合我们的需要。如果我们需要 context1、context2要不同时执行成功,要不都不成功,我们需要对代码作小小的调整,如用下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save Changes but don't discard yet context1.SaveChanges(false); //Save Changes but don't discard yet context2.SaveChanges(false); //if we get here things are looking good. scope.Complete(); context1.AcceptAllChanges(); context2.AcceptAllChanges(); } |
我们用SaveChanges(false)先将必要的数据库操作命令发送给数据库,这是注意context1与context2并没有真正发生改变,如果事务终止,自动回滚,两者的更改都没有真正提交到数据库,所以是可以成功回滚的。 在Entity Framework 中使用SaveChanges()是很频繁的,单次修改或删除数据后调用SaveChanges()返回影响记录数。 要使用批量修改或者批量删除数据,就需要SaveChanges(false)+AcceptAllChanges()方法了。 SaveChanges(false) 只是通知EF需要对数据库执行的操作,在内存中是属于挂起状态,在必要的时候是可以撤销的,比如AcceptAllChange()提交为真正成功,EF将撤销SaveChanges(false)的操作。 而在处理分布式事务操作的时候,就有必要使用TransactionScope 来处理了,很多时候我们会这样写:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save and discard changes context1.SaveChanges(); //Save and discard changes context2.SaveChanges(); //if we get here things are looking good. scope.Complete(); } |
但是这样写是有风险的,假 如context1.SaveChanges()成功了,context2.SaveChanges()却是有问题的,我们在scope.Complete()提交事务的时候就会终止,而Context1已经成功执行了 这可能不一定符合我们的需要。如果我们需要 context1、context2要不同时执行成功,要不都不成功,我们需要对代码作小小的调整,如用下面的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
using (TransactionScope scope = new TransactionScope()) { //Do something with context1 //Do something with context2 //Save Changes but don't discard yet context1.SaveChanges(false); //Save Changes but don't discard yet context2.SaveChanges(false); //if we get here things are looking good. scope.Complete(); context1.AcceptAllChanges(); context2.AcceptAllChanges(); } |
我们用SaveChanges(false)先将必要的数据库操作命令发送给数据库,这是注意context1与context2并没有真正发生改变,如果事务终止,自动回滚,两者的更改都没有真正提交到数据库,所以是可以成功回滚的。 To use TransactionScope class you need to keep two things in mind You need to add System.Transactions reference to your project Make sure the Windows service “Distributed Transaction Coordinator” is up and running. from:http://www.cnblogs.com/hyl8218/archive/2011/10/10/2205576.html
View Details前言 本文开始前我将循序渐进先了解下实现EF中的异步,并将重点主要是放在EF中的事务以及性能优化上,希望通过此文能够帮助到你。 异步 既然是异步我们就得知道我们知道在什么情况下需要使用异步编程,当等待一个比较耗时的操作时,可以用异步来释放当前的托管线程而无需等待,从而在管理线程中不需要花费额外的时间,也就是不会阻塞当前线程的运行。 在客户端如:Windows Form以及WPF应用程序中,当执行异步操作时,则当前线程能够保持用户界面持续响应。在服务器端如:ASP.NET应用程序中,执行异步操作可以用来处理多个请求,可以提高服务器的吞吐量等等。 在大部分应用程序中,对于比较耗时的操作用异步来实现可能会有一些改善,但是若你不多加考虑,动不动就用异步反而会得到相反的效果以及对应用程序也是致命的。 鉴于上述描述,我们接下来通过EF实现异步来加深理解。(想想还是把所用类及映射给出来,以免没看过前面的文章的同仁不知所云。) Student(学生)类:
1 2 3 4 5 6 7 8 9 10 |
public class Student { public int Id { get; set; } public string Name { get; set; } public int FlowerId { get; set; } public virtual Flower Flower { get; set; } } |
Flower(小红花)类
1 2 3 4 5 6 7 8 |
public class Flower { public int Id { get; set; } public string Remark { get; set; } public virtual ICollection<Student> Students { get; set; } } |
相关映射:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class StudentMap : EntityTypeConfiguration<Student> { public StudentMap() { ToTable("Student"); HasKey(key => key.Id); HasRequired(p => p.Flower).WithMany(p => p.Students).HasForeignKey(p => p.FlowerId); } } public class FlowerMap:EntityTypeConfiguration<Flower> { public FlowerMap() { ToTable("Flower"); HasKey(p => p.Id); } } |
接下来我们添加相关数据并实现异步:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
static async Task AsycOperation() { using (var ctx = new EntityDbContext()) { ctx.Set<Student>().FirstOrDefault(d => d.Name == "xpy0928"); Console.WriteLine("准备添加数据,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); (3) Thread.Sleep(3000); ctx.Set<Student>().Add(new Student() { Flower = new Flower() { Remark = "so bad" }, Name = "xpy0928" }); await ctx.SaveChangesAsync(); Console.WriteLine("数据保存完成,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); (4) } } |
接下来就是在控制台进行调用以及输出:
1 2 3 4 5 6 7 |
Console.WriteLine("执行异步操作之前,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); (1) AsycOperation(); Console.WriteLine("执行异步操作后,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); (2) Console.ReadKey(); |
这段代码不难理解,基于我们对于异步的理解,输出顺序应该是(1)(3)(2)(4),结果如我们预期一样,如下: 我们知道await关键字的作用是: 在线程池中新起一个将被执行的工作线程Task,当要执行IO操作时则会将工作线程归还给线程池,因此await所在的方法不会被阻塞。当此任务完成后将会执行该关键字之后代码! 所以当执行到await关键字时,此时会在线程池中新开一个工作线程Id为11,此时要执行添加数据,所以此时将线程归还给主线程,不阻塞主线程的运行所以就出现先执行(2)而不是先执行(4)。 接下来看一个稍微在上述基础上经过改造的方法。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
static async Task AsycOperation() { using (var ctx = new EntityDbContext()) { ctx.Set<Student>().FirstOrDefault(d => d.Name == "xpy0928"); Console.WriteLine("准备添加数据,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); Thread.Sleep(3000); ctx.Set<Student>().Add(new Student() { Flower = new Flower() { Remark = "so bad" }, Name = "xpy09284" }); await ctx.SaveChangesAsync(); Console.WriteLine("数据保存完成,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); Console.WriteLine("开始执行查询,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); var students = await (from stu in ctx.Set<Student>() select stu).ToListAsync(); Console.WriteLine("遍历获得所有学生的姓名,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); foreach (var stu in students) { Console.WriteLine("学生姓名为:{0}", stu.Name); } } } |
接下来在控制台中进行如下调用:
1 2 3 4 5 6 7 8 9 |
Console.WriteLine("执行异步操作之前,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); var task = AsycOperation(); ; task.Wait(); Console.WriteLine("执行异步操作后,当前线程Id为{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadKey(); |
接下我们进行打印如下: 上述至于为什么不是执行【执行异步操作后,当前线程Id为10】然后执行【遍历获得所有学生的姓名,当前线程Id为12】,想必大家能清楚的明白,是执行上述 task.Wait() 的缘故,必须进行等待当前任务执行完再执行主线程后面的输出。 对于处理EF中的异步没有太多去探索的东西,基本就是调用EF中对应的异步方法即可,重点是EF中的事务,请继续往下看: *事务 默认情况下 可能我们未曾注意到,其实在EF的所有版本中,当我们调用SaveChanges方法来执行增、删、改时其操作内部都用一个transaction包裹着。不信,如下图,当添加数据时: 对于上下文中的 ExecuteSqlCommand() 方法默认情况下也是用transaction包裹着命令(Command),其有重载我们可以显示指定执行事务还是不确定执行事务。 在此上两种情况下,事务的隔离级别是数据库提供者认为的默认设置的任何隔离级别,例如在SQL Server上默认是READ COMMITED(读提交)。 EF对于任何查询都不会用transaction来进行包裹。 在EF 6.0版本以上,EF一直保持数据库连接打开,因为要启动一个transaction必须是在数据库连接打开的前提下,同时这也就意味着我们执行多个操作在一个transaction的唯一方式是要么使用 TransactionScope 要么使用 ObjectContext.Connection 属性并且启动调用Open()方法以及BeginTransaction()方法直接返回EntityConnection对象。如果你在底层数据库连接上启动了transaction,再调用API连接数据库可能会失败。 概念 在开始学习事务之前我们先了解两个概念: Database.BeginTransaction() :它是在一个已存在的DbContext上下文中对于我们去启动和完成transactions的一种简单方式,它允许多个操作组合存在在相同的transaction中,所以要么提交要么全部作为一体回滚,同时它也允许我们更加容易的去显示指定transaction的隔离级别。 Dtabase.UseTransaction() :它允许DbContext上下文使用一个在EF实体框架之外启动的transaction。 在相同上下文中组合几个操作到一个transaction Database.BeginTransaction有两种重载——一种是显示指定隔离级别,一种是无参数使用来自于底层数据库提供的默认隔离级别,两种都是返回一个DbContextTransaction对象,该对象提供了事务提交(Commint)以及回滚(RollBack)方法直接表现在底层数据库上的事务提交以及事务回滚上。 DbContextTransaction一旦被提交或者回滚就会被Disposed,所以我们使用它的简单的方式就是使用using(){}语法,当using构造块完成时会自动调用Dispose()方法。 根据上述我们现在通过两个步骤来对学生进行操作,并在同一transaction上提交。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
using (var ctx = new EntityDbContext()) { using (var ctxTransaction = ctx.Database.BeginTransaction()) { try { ctx.Database.Log = Console.WriteLine; ctx.Database.ExecuteSqlCommand("update student set name='xpy0929'"); var list = ctx.Set<Student>().Where(p => p.Name == "xpy0929").ToList(); list.ForEach(d => { d.Name = "xpy0928"; }); ctx.SaveChanges(); ctxTransaction.Commit(); } catch (Exception) { ctxTransaction.Rollback(); } } } |
我们通过控制台输出SQL日志查看提交事务成功如下: 【注意】 要开始一个事务必须保持底层数据库连接是打开的,如果数据库不总是打开的我们可以通过 BeginTransaction() 方法将打开数据库连接,如果 DbContextTransaction 打开了数据库,当调用Disposed()方法时将会关闭数据库连接。 注意事项 当用EF上下文中的 Database.ExecuteSqlCommand 方法来对数据库进行如下操作时
1 2 3 4 5 6 7 |
using (var ctx = new EntityDbContext()) { var sqlCommand = String.Format("ALTER DATABASE {0} SET SINGLE_USER WITH ROLLBACK IMMEDIATE", "DBConnectionString"); ctx.Database.ExecuteSqlCommand(sqlCommand); } |
此时将会报错如下: 上述已经讲过此方法会被Transaction包裹着,所以导致出错,但是此方法有重载,我们进行如下设置即可
1 |
ctx.Database.ExecuteSqlCommand(TransactionalBehavior.DoNotEnsureTransaction,sqlCommand); |
将一个已存在的事务添加到上下文中 有时候我们可能需要事务的作用域更加广一点,当然是在同一数据库上但是是在EF之外完全进行操作。基于此,此时我们必须手动打开底层的数据库连接来启动事务,同时通知EF使用我们手动打开的连接来使现有的事务连接在此连接上,这样就达到了使用在EF之外的事务。 为了实现上述在EF之外使用事务我们必须在DbContext上下文中的派生类的构造器中关闭自身的连接而使用我们传入的连接。 第一步 上下文中关闭EF连接使用底层连接。 代码如下:
1 2 3 |
public EntityDbContext(DbConnection con) : base(con, contextOwnsConnection: false) { } |
第二步 启动Transcation(如果我们想避免默认设置我们可以手动设置隔离级别),通知EF一个已存在的Transaction已经在我们手动的设置的底层连接上启动。 […]
View Details