asp.net core系列 35 EF保存數據(2) -- EF系列結束


一.事務

  (1) 事務接着上篇繼續講完。如果使用了多種數據訪問技術,來訪問關系型數據庫,則可能希望在這些不同技術所執行的操作之間共享事務。下面示例顯示了如何在同一事務中執行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。 

using (var connection = new SqlConnection(connectionString))
{
    //使用ado.net 打開數據庫連接
    connection.Open();

   //使用ado.net 開啟事務
    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事務結合ado.net事務
                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

    如果需要跨大作用域進行協調,則可以使用分布式事務(跨庫事務)TransactionScope,它可協調跨多個資源管理器的事務。存在於ADO.NET 中的System.Transactions命令空間。此功能是 EF Core 2.1 中的新增功能。雖然該功能在 .NET Framework 的 ADO.NET 提供程序之間十分常見,但最近才將 API 添加到 .NET Core,因此支持並未得到廣泛應用。

//設置事務隔離級別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
        }
    }
}

 

二. 異步保存

  關於使用異步的注意事項和優勢,在第33篇 EF查詢數據中有講到。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();
    }
}

  

三.不同上下文的實體狀態判斷

  有時會使用一個上下文實例查詢實體,然后使用其他上下文實例對其進行保存。 這通常在“斷開連接”的情況下發生,例如 Web 應用程序,此情況下實體被查詢、發送到客戶端被修改、在請求中發送回服務器,然后進行保存。 在這種情況下,第二個上下文實例需要知道實體是新實體(應插入)還是現有實體(應更新)。

  

  3.1標識新實體

    下面介紹了幾中方式確定是插入還是更新的實體情況:

     (1)使用自動生成的鍵

      可以理解為在數據庫端設置ID鍵自增長,可以通過鍵值來判斷是新增還是修改。

        /// <summary>
        ///(1)使用鍵的內置方法來檢查 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)  使用其它鍵

      未自動生成鍵值時,需要使用其他某種機制來確定新實體。 有以下兩種常規方法(查詢實體 或 從客戶端傳遞標志)。若要查詢實體,只需使用 Find 方法, 例如下所示:

        public static bool IsItNew(BloggingContext context, Blog blog)
        => context.Blogs.Find(blog.BlogId) == null;

 

  3.2 保存單個實體

    如果知道是需要插入還是需要更新,則可以相應地使用 Add 或 Update(之前是新增還是修改,是根據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();
}

    如果實體不使用自動生成的鍵,則應用程序必須確定是應插入實體還是應更新實體:例如:    

public static void InsertOrUpdate(BloggingContext context, Blog blog)
{
    var existingBlog = context.Blogs.Find(blog.BlogId);
    if (existingBlog == null)
    {
        context.Add(blog);
    }
    else
    {
       // SetValues 調用將根據需要,標記要更新的實體屬性。原理是:要更新的實體與之前查詢的實體進行比較,只會更新實際發生更改的列
        context.Entry(existingBlog).CurrentValues.SetValues(blog);
    }
    context.SaveChanges();
}

 

四. 設置SQL Server IDENTITY列中的顯式值

  對於大多數情況,是由數據庫生成自增長ID。如果要將顯式值插入SQL Server IDENTITY列,需要在調用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();
    }
}

 

 

 

參考文獻

  TransactionScope介紹

  EF第三方擴展工具

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM