Linq to SQL 语法记录....并发写事务

zz/2023/6/3 15:57:02

检测并发

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

select * from products where categoryid=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)]

       为这两列标注不需要进行更新检测。假设现在产品价格和库存分别是2732。那么,我们启动程序(设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为2830了,继续程序可以发现价格和库存分别是2831。价格+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。也就是说,放弃当前的更新,所有更新以原先更新为准。

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

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

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

       假设现在产品价格和库存分别是2732。那么,启动程序(在ctx.SubmitChanges(ConflictMode.ContinueOnConflict)这里设置端点),然后运行UPDATE语句,把价格+1,库存-2,然后价格和库存分别为2830了,继续程序可以发现价格和库存分别是2731。产品价格没有变化,库存-1了,SQL语句的更新被放弃了。

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

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

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

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

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); // 原先更新有效,冲突字段以当前更新为准

    }

}

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

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

ctx.SubmitChanges(ConflictMode.FailOnFirstConflict);

       表示第一次发生冲突的时候就不再继续了,然后并且去除最后的ctx.SubmitChanges();语句。说明在第一条记录失败后,后续的并发冲突就不再处理了。

事务处理

       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();

        }

转载于:https://www.cnblogs.com/cryloo/archive/2008/10/21/1316094.html

http://www.ngui.cc/zz/483579.html

相关文章

使用 Adobe AIR 管理 WordPress 评论

Moderator 是一个基于 Adobe AIR 的应用程序&#xff0c;可以运行在桌面&#xff0c;它包含一个 WordPress 插件&#xff0c;用户可以直接在桌面上管理 WordPress 博客中的用户评论&#xff0c;可以删除不当评论&#xff0c;或将某些评论设为 Spam。 安装之后&#xff0c;Moder…

关于VS.NET RSACryptoServiceProvider的疑惑

RSA的安全性依赖于大数难于分解这一特点。公钥和私钥都是两个大素数&#xff08;大于100个十进制位&#xff09;的函数。据猜测&#xff0c;从一个密钥和密文推断出明文的难度等同于分解两个大素数的积。 密钥对的产生。选择两个大素数&#xff0c;p 和q 。计算&#xff1a;n …

.NET C# 群发 HTML格式 带附件 中文发送者 密送 抄送 的邮件

转载于:https://www.cnblogs.com/sjcatsoft/archive/2008/11/08/1329672.html

GRIDVIEW排序 动态实现和静态实现

用了GRIDVIEW一段时间&#xff0c;发现很多人都在问GRIDVIEW的排序功能&#xff0c;有些朋友在我的QQ群(13536330)里面问我&#xff0c;我觉得有三种方法可以实现&#xff0c;但本文我只讲两种&#xff0c;相信可以满足大家的需要了吧。1、静态实现&#xff08;直接用GRIDVIEW和…

C++中XMLHTTPRequest异步使用onreadystatuschange

首先需要编写一个类&#xff0c;实现IDispatch接口&#xff0c;然后把这个类传进去&#xff0c;代码如下&#xff1a;#ifndef XMLHTTPEVENTSINK_H_#define XMLHTTPEVENTSINK_H_ #include "msxml.h"#include <winnt.h>#include <wtypes.h>#include <wi…

自定义用户控件显示属性分类、描述、默认值

在网上查了相关资料&#xff0c;大概例子如下&#xff1a;using System.ComponentModel; bool _IsCheck true; [Bindable(true), DefaultValue(true), Category("Flash 播放器窗口模式"), Description("描述中")] public bool IsCheck { …

.NET Remoting Security使用小结 – TcpChannel

谈到Security需要从下面四个方面考虑&#xff1a; 1. Authentication&#xff1a;防止非法用户的调用。 2. Authorization&#xff1a;防止合法但权限不够的用户调用。 3. Encryption&#xff1a;防止数据在传输过程中被窃取。 4. Sign&#xff1a;防止…

Flash Socket通信的安全策略问题

弄了半天没有解决&#xff0c;在ide里发布没为&#xff0c;放到web上就安全问题。哎 记个笔记&#xff1a;虽然还没有搞定. 参考文章 flash xmlsocket policy 问题Policy file changes in Flash Player 9Setting up a socket policy file serverUnderstanding Flash Player 9 A…

1.3.2 Jetty 的基本配置(2)

1.3.2 Jetty 的基本配置&#xff08;2&#xff09; 3. 配置 JNDI 绑定 Jetty 同样可以整合 DBCP、 C3PO 等数据源来提供容器管理的数据源。提供容器管理的数据源&#xff0c;只是 Jetty JNDI 绑定功能之一。 下面介绍如何在 Jetty 绑定 JNDI&#xff0c;以及 JNDI 的使用。 增加…

潘石屹语录

★我是一个纯粹的商人&#xff0c;不管做什么行业&#xff0c;只要纯粹就好&#xff0c;人就怕不纯粹。 ★不赚钱的商人是不道德的&#xff0c;不赚钱你就只能确保自己的生活&#xff0c;不能给员工好的工资福利待遇&#xff0c;不能给国家上缴利税&#xff0c;不能给客户带来实…