一.事務(wù)
(1) 事務(wù)接著上篇繼續(xù)講完。如果使用了多種數(shù)據(jù)訪問技術(shù),來訪問關(guān)系型數(shù)據(jù)庫,則可能希望在這些不同技術(shù)所執(zhí)行的操作之間共享事務(wù)。下面示例顯示了如何在同一事務(wù)中執(zhí)行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。
using (var connection = new SqlConnection(connectionString))
{
//使用ado.net 打開數(shù)據(jù)庫連接
connection.Open();
//使用ado.net 開啟事務(wù)
using (var transaction = connection.BeginTransaction())
{
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.Transaction = transaction;
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
//EF事務(wù)結(jié)合ado.net事務(wù)
context.Database.UseTransaction(transaction);
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}
// Commit transaction if all commands succeed, transaction will auto-rollback when disposed if either commands fails
transaction.Commit();
}
catch (System.Exception)
{
// TODO: Handle failure
}
}
}
(2) 使用 System.Transactions
如果需要跨大作用域進(jìn)行協(xié)調(diào),則可以使用分布式事務(wù)(跨庫事務(wù))TransactionScope,它可協(xié)調(diào)跨多個資源管理器的事務(wù)。存在于ADO.NET 中的System.Transactions命令空間。此功能是 EF Core 2.1 中的新增功能。雖然該功能在 .NET Framework 的 ADO.NET 提供程序之間十分常見,但最近才將 API 添加到 .NET Core,因此支持并未得到廣泛應(yīng)用。
//設(shè)置事務(wù)隔離級別IsolationLevel.ReadCommitted
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions { IsolationLevel = IsolationLevel.ReadCommitted }))
{
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
try
{
// Run raw ADO.NET command in the transaction
var command = connection.CreateCommand();
command.CommandText = "DELETE FROM dbo.Blogs";
command.ExecuteNonQuery();
// Run an EF Core command in the transaction
var options = new DbContextOptionsBuilder<BloggingContext>()
.UseSqlServer(connection)
.Options;
using (var context = new BloggingContext(options))
{
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
context.SaveChanges();
}
// Commit transaction if all commands succeed, transaction will auto-rollback
// when disposed if either commands fails
scope.Complete();
}
catch (System.Exception)
{
// TODO: Handle failure
}
}
}
二. 異步保存
關(guān)于使用異步的注意事項和優(yōu)勢,在第33篇 EF查詢數(shù)據(jù)中有講到。Entity Framework Core 提供了 DbContext.SaveChangesAsync() 異步替代了 DbContext.SaveChanges() 同步方法。下面是一個保存,使用異步示例
public static async Task AddBlogAsync(string url)
{
using (var context = new BloggingContext())
{
var blog = new Blog { Url = url };
context.Blogs.Add(blog);
await context.SaveChangesAsync();
}
}
三.不同上下文的實體狀態(tài)判斷
有時會使用一個上下文實例查詢實體,然后使用其他上下文實例對其進(jìn)行保存。 這通常在“斷開連接”的情況下發(fā)生,例如 Web 應(yīng)用程序,此情況下實體被查詢、發(fā)送到客戶端被修改、在請求中發(fā)送回服務(wù)器,然后進(jìn)行保存。 在這種情況下,第二個上下文實例需要知道實體是新實體(應(yīng)插入)還是現(xiàn)有實體(應(yīng)更新)。
3.1標(biāo)識新實體
下面介紹了幾中方式確定是插入還是更新的實體情況:
(1)使用自動生成的鍵
可以理解為在數(shù)據(jù)庫端設(shè)置ID鍵自增長,可以通過鍵值來判斷是新增還是修改。
/// <summary>
///(1)使用鍵的內(nèi)置方法來檢查 true: 新增(上下文類中)
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public bool IsItNew( object entity)
=> !this.Entry(entity).IsKeySet;
// (2)已知類型檢查 true: 新增
public bool IsItNew(Blog blog)
=> blog.BlogId <= 0;
// b is false 修改實體
var blog = BloggingContext.Blogs.First();
bool b = BloggingContext.IsItNew(blog);
//b is true 新增實體
var blog = new Blog() { Url = "www.baidu.com" };
bool b = BloggingContext.IsItNew(blog);
(2) 使用其它鍵
未自動生成鍵值時,需要使用其他某種機(jī)制來確定新實體。 有以下兩種常規(guī)方法(查詢實體 或 從客戶端傳遞標(biāo)志)。若要查詢實體,只需使用 Find 方法, 例如下所示:
public static bool IsItNew(BloggingContext context, Blog blog)
=> context.Blogs.Find(blog.BlogId) == null;
3.2 保存單個實體
如果知道是需要插入還是需要更新,則可以相應(yīng)地使用 Add 或 Update(之前是新增還是修改,是根據(jù)ChangeTracker跟蹤器自動檢測的,因為是同一個下下文而且實體有主鍵)如下所示:
public static void Insert(DbContext context, object entity)
{
context.Add(entity);
context.SaveChanges();
}
public static void Update(DbContext context, object entity)
{
context.Update(entity);
context.SaveChanges();
}
如果實體不使用自動生成的鍵,則應(yīng)用程序必須確定是應(yīng)插入實體還是應(yīng)更新實體:例如:
public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
var existingBlog = context.Blogs.Find(blog.BlogId);
if (existingBlog == null)
{
context.Add(blog);
}
else
{
// SetValues 調(diào)用將根據(jù)需要,標(biāo)記要更新的實體屬性。原理是:要更新的實體與之前查詢的實體進(jìn)行比較,只會更新實際發(fā)生更改的列
context.Entry(existingBlog).CurrentValues.SetValues(blog);
}
context.SaveChanges();
}
四. 設(shè)置SQL Server IDENTITY列中的顯式值
對于大多數(shù)情況,是由數(shù)據(jù)庫生成自增長ID。如果要將顯式值插入SQL Server IDENTITY列,需要在調(diào)用SaveChanges()之前,手動啟用IDENTITY_INSERT。如下所示:
using (var context = new EmployeeContext())
{
context.Employees.Add(new Employee { EmployeeId = 100, Name = "John Doe" });
context.Employees.Add(new Employee { EmployeeId = 101, Name = "Jane Doe" });
context.Database.OpenConnection();
try
{
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees ON");
context.SaveChanges();
context.Database.ExecuteSqlCommand("SET IDENTITY_INSERT dbo.Employees OFF");
}
finally
{
context.Database.CloseConnection();
}
}
參考文獻(xiàn)