Entity Framework中的事务
在这里您将了解EF 6 & EF Core中的事务。
在Entity Framework中,SaveChanges()方法在内部创建一个事务,并将所有的INSERT、UPDATE和DELETE操作包装在其中。多个SaveChanges()调用,创建单独的事务,执行CRUD操作,然后提交每个事务。下面的示例演示了这一点。
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
var standard = context.Standards.Add(new Standard() { StandardName = "1st Grade" });
context.Students.Add(new Student()
{
FirstName = "Rama",
StandardId = standard.StandardId
});
context.SaveChanges();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
}
上面的示例将向控制台显示以下输出。
在上面的示例中,我们将所有数据库命令记录到控制台。我们添加了一个新的Standard实体和Student实体,并使用SaveChanges()方法将它们保存到数据库中。这将创建一个新的事务,并在事务中为Standard和Student实体执行INSERT命令并提交它们。在此之后,我们添加一个新的Course实体并调用SaveChanges()。这将创建另一个事务,执行INSERT命令,然后提交事务。因此,每个SaveChanges()方法调用都会创建一个新的事务,并在其中执行数据库命令。
在单个事务中进行多个savechange
EF 6和EF Core允许我们使用以下方法创建或使用多个SaveChanges()调用单个事务:
- DbContext.Database.BeginTransaction(): 为底层数据库创建一个新的事务,并允许我们使用多个SaveChanges方法调用提交或回滚对数据库所做的更改。
- DbContext.Database.UseTransaction(): 允许我们传递在上下文对象范围之外创建的现有事务对象。这将允许EF在外部事务对象中执行命令。或者,传入null来清除框架对该事务的了解。
DbContext.Database.BeginTransaction()
下面的例子演示了使用BeginTransaction()创建一个新的事务对象,然后使用多个SaveChanges()调用。
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
using (DbContextTransaction transaction = context.Database.BeginTransaction()) //开启事务
{
try
{
var standard = context.Standards.Add(new Standard() { StandardName = "1st Grade" });
context.Students.Add(new Student()
{
FirstName = "Rama2",
StandardId = standard.StandardId
});
context.SaveChanges();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit(); //提交事务
}
catch (Exception ex)
{
transaction.Rollback(); //事务回滚
Console.WriteLine("Error occurred.");
}
}
}
在上面的例子中,我们创建了新的Standard、Student和Course实体,并通过调用两个SaveChanges()将它们保存到数据库中,这两个SaveChanges()在一个事务中执行INSERT命令。下图显示了输出日志。
注意:你可以在DbContext.Database.BeginTransaction()方法中指定不同的隔离级别。访问MSDN可了解有关隔离级别(https://msdn.microsoft.com/en-us/library/system.data.isolationlevel(v=vs.113).aspx)的更多信息。
如果发生异常,则对数据库所做的所有更改都将回滚。
using (var context = new SchoolContext())
{
context.Database.Log = Console.Write;
using (DbContextTransaction transaction = context.Database.BeginTransaction())
{
try
{
var standard = context.Standards.Add(new Standard() { StandardName = "1st Grade" });
context.Students.Add(new Student()
{
FirstName = "Rama",
StandardId = standard.StandardId
});
context.SaveChanges();
// throw exectiopn to test roll back transaction
throw new Exception();
context.Courses.Add(new Course() { CourseName = "Computer Science" });
context.SaveChanges();
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine("Error occurred.");
}
}
}
在上面的例子中,我们在第一次SaveChanges()调用之后抛出一个异常。这将执行一个catch块,在其中调用RollBack()方法来回滚对数据库所做的任何更改。输出结果如下图所示。
DbContext.Database.UseTransaction()
DbContext.Database.UseTransaction()方法允许我们使用在上下文对象范围之外创建的现有事务。如果使用UseTransaction()方法,则上下文将不会创建内部事务对象,而是使用提供的事务。
下面的示例演示了使用EF 6 code-first的UseTransaction()方法。
private static void Main(string[] args)
{
string providerName = "System.Data.SqlClient";
string serverName = ".";
string databaseName = "SchoolDB";
// Initialize the connection string builder for the SQL Server provider.
SqlConnectionStringBuilder sqlBuilder =
new SqlConnectionStringBuilder();
// Set the properties for the data source.
sqlBuilder.DataSource = serverName;
sqlBuilder.InitialCatalog = databaseName;
sqlBuilder.IntegratedSecurity = true;
using (SqlConnection con = new SqlConnection(sqlBuilder.ToString()))
{
con.Open();
using (SqlTransaction transaction = con.BeginTransaction())
{
try
{
using (SchoolContext context = new SchoolContext(con, false))
{
context.Database.UseTransaction(transaction);
context.Students.Add(new Student() { Name = "Ravi" });
context.SaveChanges();
}
using (SchoolContext context = new SchoolContext(con, false))
{
context.Database.UseTransaction(transaction);
context.Grades.Add(new Standard() { GradeName = "Grade 1", Section = "A" });
context.SaveChanges();
}
transaction.Commit();
}
catch (Exception ex)
{
transaction.Rollback();
Console.WriteLine(ex.InnerException);
}
}
}
}
下面是上面例子中使用的一个SchoolContext类。
public class SchoolContext : DbContext
{
public SchoolContext(DbConnection con, bool contextOwnsConnection) :base(con, contextOwnsConnection)
{
}
public SchoolContext(): base("SchoolDB")
{
Database.SetInitializer<SchoolContext>(new CreateDatabaseIfNotExists<SchoolContext>());
}
public DbSet<Student> Students { get; set; }
public DbSet<Standard> Grades { get; set; }
public DbSet<Course> Courses { get; set; }
}
参考
https://www.entityframeworktutorial.net/
https://msdn.microsoft.com/