Linq to sql并发与事务

简介: 本文转载:http://www.cnblogs.com/lovecherry/archive/2007/08/20/862365.html 检测并发          首先使用下面的SQL语句查询数据库的产品表: select * from products where categoryid=1          查询结果如下图:          为了看起来清晰,我已经事先把所有分类为1产品的价格和库存修改为相同值了。

本文转载:http://www.cnblogs.com/lovecherry/archive/2007/08/20/862365.html

检测并发

 

       首先使用下面的SQL语句查询数据库的产品表:

select * from products where categoryid=1

 

       查询结果如下图:

 

       为了看起来清晰,我已经事先把所有分类为1产品的价格和库存修改为相同值了。然后执行下面的程序:

        var query = from p in ctx.Products where p.CategoryID == 1 select p;

        foreach (var p in query)

            p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);

        ctx.SubmitChanges(); // 在这里设断点

 

       我们使用调试方式启动,由于设置了断点,程序并没有进行更新操作。此时,我们在数据库中运行下面的语句:

update products

set unitsinstock = unitsinstock -2, unitprice= unitprice + 1

where categoryid = 1

 

       然后在继续程序,会得到修改并发(乐观并发冲突)的异常,提示要修改的行不存在或者已经被改动。当客户端提交的修改对象自读取之后已经在数据库中发生改动,就产生了修改并发。解决并发的包括两步,一是查明哪些对象发生并发,二是解决并发。如果你仅仅是希望更新时不考虑并发的话可以关闭相关列的更新验证,这样在这些列上发生并发就不会出现异常:

[Column(Storage="_UnitsInStock", DbType="SmallInt", UpdateCheck = UpdateCheck.Never)]

[Column(Storage="_UnitPrice", DbType="Money", UpdateCheck = UpdateCheck.Never)]

 

       为这两列标注不需要进行更新检测。假设现在产品价格和库存分别是27和32。那么,我们启动程序(设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。价格+1是之前更新的功劳,库存最终是-1是我们程序之后更新的功劳。当在同一个字段上(库存)发生并发冲突的时候,默认是最后的那次更新获胜。

 

解决并发

 

       如果你希望自己处理并发的话可以把前面对列的定义修改先改回来,看下面的例子:

        var query = from p in ctx.Products where p.CategoryID == 1 select p;

        foreach (var p in query)

            p.UnitsInStock = Convert.ToInt16(p.UnitsInStock - 1);

        try

        {

            ctx.SubmitChanges(ConflictMode.ContinueOnConflict);

        }

        catch (ChangeConflictException)

        {

            foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)

            {

                Product p = (Product)cc.Object;

                Response.Write(p.ProductID + "<br/>");

                cc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准

            }

        }

        ctx.SubmitChanges();

 

       首先可以看到,我们使用try{}catch{}来捕捉并发冲突的异常。在SubmitChanges的时候,我们选择了ConflictMode.ContinueOnConflict选项。也就是说遇到并发了还是继续。在catch{}中,我们从ChangeConflicts中获取了并发的对象,然后经过类型转化后输出了产品ID,然后选择的解决方案是RefreshMode.OverwriteCurrentValues。也就是说,放弃当前的更新,所有更新以原先更新为准。

       我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和30。之前SQL语句库存-2生效了,而我们程序的更新(库存-1)被放弃了。在页面上也显示了所有分类为1的产品ID(因为我们之前的SQL语句是对所有分类为1的产品都进行修改的)。

       然后,我们来修改一下解决并发的方式:

cc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

 

       来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是27和31。产品价格没有变化,库存-1了,都是我们程序的功劳,SQL语句的更新被放弃了。

       然后,我们再来修改一下解决并发的方式:

cc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准

 

       来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别是28和31。这就是默认方式,在保持原先更新的基础上,对于发生冲突的字段以最后更新为准。

       我们甚至还可以针对不同的字段进行不同的处理策略:

foreach (ObjectChangeConflict cc in ctx.ChangeConflicts)

{

    Product p = (Product)cc.Object;

    foreach (MemberChangeConflict mc in cc.MemberConflicts)

    {

        string currVal = mc.CurrentValue.ToString();

        string origVal = mc.OriginalValue.ToString();

        string databaseVal = mc.DatabaseValue.ToString();

        MemberInfo mi = mc.Member;

        string memberName = mi.Name;

        Response.Write(p.ProductID + " " + mi.Name + " " + currVal + " " + origVal +" "+ databaseVal + "<br/>");

        if (memberName == "UnitsInStock")

            mc.Resolve(RefreshMode.KeepCurrentValues); // 放弃原先更新,所有更新以当前更新为准

        else if (memberName == "UnitPrice")

            mc.Resolve(RefreshMode.OverwriteCurrentValues); // 放弃当前更新,所有更新以原先更新为准

        else

            mc.Resolve(RefreshMode.KeepChanges); // 原先更新有效,冲突字段以当前更新为准

 

    }

}

 

       比如上述代码就对库存字段作放弃原先更新处理,对价格字段作放弃当前更新处理。我们来测试一下,假设现在产品价格和库存分别是27和32。那么,我们启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为28和30了,继续程序可以发现价格和库存分别为28和31了。说明对价格的处理确实保留了原先的更新,对库存的处理保留了当前的更新。页面上显示的结果如下图:

 

 

最后,我们把提交语句修改为:

ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

 

       表示第一次发生冲突的时候就不再继续了,然后并且去除最后的ctx.SubmitChanges();语句。来测试一下,在执行了SQL后再继续程序可以发现界面上只输出了数字1,说明在第一条记录失败后,后续的并发冲突就不再处理了。

 

事务处理

 

       Linq to sql在提交更新的时候默认会创建事务,一部分修改发生错误的话其它修改也不会生效:

        ctx.Customers.Add(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

        ctx.Customers.Add(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

        ctx.SubmitChanges();

 

       假设数据库中已经存在顾客ID为“abcde”的记录,那么第二次插入操作失败将会导致第一次的插入操作失效。执行程序后会得到一个异常,查询数据库发现“abcdf”这个顾客也没有插入到数据库中。

       如果每次更新后直接提交修改,那么我们可以使用下面的方式做事务:

        if (ctx.Connection != null) ctx.Connection.Open();

        DbTransaction tran = ctx.Connection.BeginTransaction();

        ctx.Transaction = tran;

        try

        {

            CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

            CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

            tran.Commit();

        }

        catch

        {

            tran.Rollback();

        }

 

    private void CreateCustomer(Customer c)

    {

        ctx.Customers.Add(c);

        ctx.SubmitChanges();

    }

 

       运行程序后发现增加顾客abcdf的操作并没有成功。或者,我们还可以通过TransactionScope实现事务:

        using (TransactionScope scope = new TransactionScope())

        {

            CreateCustomer(new Customer { CustomerID = "abcdf", CompanyName = "zhuye" });

            CreateCustomer(new Customer { CustomerID = "abcde", CompanyName = "zhuye" });

            scope.Complete();

        }

 

      

目录
相关文章
|
SQL 数据库 开发者
MSSQL性能调优实战技巧:索引优化、SQL语句微调与并发控制策略
在Microsoft SQL Server(MSSQL)的管理与优化中,性能调优是一项复杂但至关重要的任务
|
3月前
|
存储 SQL 关系型数据库
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
mysql底层原理:索引、慢查询、 sql优化、事务、隔离级别、MVCC、redolog、undolog(图解+秒懂+史上最全)
|
SQL 监控 数据库
MSSQL性能调优实战策略:索引优化、SQL语句重构与并发控制
在Microsoft SQL Server(MSSQL)的管理和优化过程中,性能调优是确保数据库高效运行、满足业务需求的重要环节
|
SQL 运维 监控
MSSQL性能调优实战:索引优化、SQL查询效率提升与并发控制策略
在Microsoft SQL Server(MSSQL)的日常运维与性能优化中,精准的策略与技巧是实现高效数据库管理的关键
|
SQL 存储 数据库
MSSQL性能调优实战:索引优化、SQL语句精调与高效并发处理
在Microsoft SQL Server(MSSQL)的性能调优征途中,索引优化、SQL语句的精细调整以及高效并发处理是三大核心策略
|
SQL 监控 数据库
MSSQL性能调优实战技巧:索引优化策略、SQL查询重构与并发控制详解
在Microsoft SQL Server(MSSQL)的管理与优化过程中,性能调优是确保数据库高效运行的关键环节
|
SQL 数据库 数据安全/隐私保护
SQL Server数据库Owner导致事务复制log reader job无法启动的解决办法
【8月更文挑战第14天】解决SQL Server事务复制Log Reader作业因数据库所有者问题无法启动的方法:首先验证数据库所有者是否有效并具足够权限;若非,使用`ALTER AUTHORIZATION`更改为有效登录名。其次,确认Log Reader使用的登录名拥有读取事务日志所需的角色权限。还需检查复制配置是否准确无误,并验证Log Reader代理的连接信息及参数。重启SQL Server Agent服务或手动启动Log Reader作业亦可能解决问题。最后,审查SQL Server错误日志及Windows事件查看器以获取更多线索。
163 0
|
SQL 监控 数据库
MSSQL性能调优实战指南:精准索引策略、SQL查询优化与高效并发控制
在Microsoft SQL Server(MSSQL)的性能调优过程中,精准索引策略、SQL查询优化以及高效并发控制是三大核心要素
|
SQL 安全 数据库
基于SQL Server事务日志的数据库恢复技术及实战代码详解
基于事务日志的数据库恢复技术是SQL Server中一个非常强大的功能,它能够帮助数据库管理员在数据丢失或损坏的情况下,有效地恢复数据。通过定期备份数据库和事务日志,并在需要时按照正确的步骤恢复,可以最大限度地减少数据丢失的风险。需要注意的是,恢复数据是一个需要谨慎操作的过程,建议在执行恢复操作之前,详细了解相关的操作步骤和注意事项,以确保数据的安全和完整。
561 0
|
SQL 运维 监控
MSSQL性能调优实战技巧:索引优化、SQL查询优化与并发控制策略
在Microsoft SQL Server(MSSQL)的运维过程中,性能调优是确保数据库高效运行、满足业务需求的关键环节

热门文章

最新文章