zoukankan      html  css  js  c++  java
  • 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第三方扩展工具

  • 相关阅读:
    C++基础学习1: C++布尔类型
    Hadoop-Yarn-框架原理及运作机制
    mapreduce shuffle 和sort 详解
    线程生命周期
    JVM 内存模型及垃圾回收
    利用Hive分析nginx日志
    mysql 30大优化策略
    hive 分组排序,topN
    Java核心卷笔记(一)
    Java 核心卷学习笔记(一)
  • 原文地址:https://www.cnblogs.com/MrHSR/p/10455037.html
Copyright © 2011-2022 走看看