欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

asp.net core系列 35 EF保存数据(2) -- EF系列结束

程序员文章站 2022-04-28 11:11:12
一.事务 (1) 事务接着上篇继续讲完。如果使用了多种数据访问技术,来访问关系型数据库,则可能希望在这些不同技术所执行的操作之间共享事务。下面示例显示了如何在同一事务中执行 ADO.NET SqlClient 操作和 Entity Framework Core 操作。 (2) 使用 System.T ......

一.事务

  (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