41实现开放式并发.docx

上传人:b****5 文档编号:29468500 上传时间:2023-07-23 格式:DOCX 页数:23 大小:550.26KB
下载 相关 举报
41实现开放式并发.docx_第1页
第1页 / 共23页
41实现开放式并发.docx_第2页
第2页 / 共23页
41实现开放式并发.docx_第3页
第3页 / 共23页
41实现开放式并发.docx_第4页
第4页 / 共23页
41实现开放式并发.docx_第5页
第5页 / 共23页
点击查看更多>>
下载资源
资源描述

41实现开放式并发.docx

《41实现开放式并发.docx》由会员分享,可在线阅读,更多相关《41实现开放式并发.docx(23页珍藏版)》请在冰豆网上搜索。

41实现开放式并发.docx

41实现开放式并发

对于可能存在多用户同时更新或删除数据的web程序来说,存在一个用户的修改覆盖另一个用户的情况。

当设计这样的程序时,选择适当的并发控制技术非常重要。

我们在实现开放式并发里已经讨论过,有三种concurrencycontrol(并发控制)的策略:

什么都不做—如果并发用户修改的是同一条记录,让最后提交的结果生效(默认的行为)

开放式并发(OptimisticConcurrency)—假定并发冲突只是偶尔发生,绝大多数的时候并不会出现;那么,当发生一个冲突时,仅仅简单的告知用户,他所作的更改不能保存,因为别的用户已经修改了同一条记录

保守式并发(PessimisticConcurrency)—假定并发冲突经常发生,并且用户不能容忍被告知自己的修改不能保存是由于别人的并发行为;那么,当一个用户开始编辑一条记录,锁定该记录,从而防止其他用户编辑或删除该记录,直到他完成并提交自己的更改

目前为止我们编辑DataList的教程都是使用的默认的策略—也就是我们让最后写的结果生效。

本章我们学习如何使用开放式并发。

第一步:

理解开放式并发是如何实现的

开放式并发控制能够确保一条记录在更新或者删除时跟它开始这次更新或修改过程时保持一致。

例如,当在一个可编辑的DataList里点击编辑按钮时,该记录的原始值从数据库中读取出来并显示在TextBox和其他Web控件中。

这些原始的值需要被保存下来。

随后,当用户完成他的修改并点击更新按钮,这些原始值加上修改后的新值发送到业务逻辑层,然后到数据访问层。

数据访问层发出一个SQL语句,它将仅仅更新那些开始编辑时的原始值根数据库中的值一致的记录。

图一描述了这些事件发生的顺序。

图1:

为了更新或删除能够成功,原始值必须与数据库中相应的值一致

有多种方法可以实现开放式并发控制(查看PeterA.Bromberg的文章 OptmisticConcurrencyUpdatingLogic,从摘要中看到许多选择)。

ADO.NET类型化数据集提供了一种应用,这只需要在配置时勾选上一个CheckBox。

开启TableAdapter的开放并发需要在TableAdapter的Update和Delete语句后面加一个比较所有原始值的WHERE从句。

我们在实现开放式并发类型化数据集(名为NorthwindOptimisticConcurrency)和一个名为ProductsOptimisticConcurrencyBLL的BLL类。

里创建了一个这样的本章我们将使用上面的方法创建一个DataList。

注意:

在继续前请阅读实现开放式并发 ,它提供了关于开放并发如何工作和如何让BLL和DAL执行开放并发的详细信息。

第二步:

创建一个可编辑和删除的DataList

使用DataList执行开放并发时,需要我们保存原始值—更新或删除时的值—然后将这些值连同新的值一起传到BLL。

首先我们来创建一个可编辑和删除的DataList,它列出product,并允许修改name和price。

打开EditDeleteDataList文件夹下的OptimisticConcurrency.aspx页。

拖一个DataList进来。

并将ID设为Products。

通过它的智能标签,创建一个名为ProductsDataSource的ObjectDataSource,并用ProductsBLL类的GetProducts()方法配置它。

在UPDATE,INSERT,DELETE标签里选择(None).

图2:

在UPDATE,INSERT,DELETE标签里选择(None)

配置完后,VisualStudio会自动创建一个ItemTemplate,它包含product的每个字段。

修改它让它只显示ProductName和UnitPrice。

我们需要在ItemTemplate里添加两个button来支持编辑和删除功能—一个删除product,一个使product可编辑。

记得这些button必须将CommandName的值设为"Edit"和"Delete",这样在被点击button时的postback中EditCommand和DelelteCommand事件才会激发。

我的ItemTemplate用

显示productname,用货币格式显示price。

你可以根据自己的喜好自定义。

注意:

由于主要将精力集中在开放并发上,本章将创建ItemTemplate和EditItemTemplate的步骤带过。

如果需要更详细的内容,请参考用DataList和Repeater来显示数据 和综叙:

在DataList里编辑和删除数据  

ASP.NET

            

           

           

LabelID="ProductNameLabel"runat="server"

           Text='<%#Eval("ProductName")%>'/>

           

           Price:

           

LabelID="UnitPriceLabel"runat="server"

           Text='<%#Eval("UnitPrice","{0:

C}")%>'/>

           

           

Buttonrunat="server"ID="EditButton"Text="Edit"

           CommandName="Edit"/>

              

           

Buttonrunat="server"ID="DeleteButton"Text="Delete"

           CommandName="Delete"/>

           

           

            

当Editbutton被点时,会postback并激发EditCommand事件。

我们马上为它创建eventhandler,将选择的product变为可编辑。

因此我们需要创建EditItemTemplate。

它包含TextBox显示productname和price,并且分别有一个RequiredFieldValidator和CompareValidator(验证条件和前面章节一样),另外我们还需要两个button—一个Update和一个Cancel—将它们的CommandName分别设为"Update"和"Cancel"。

ASP.NET

            

           Product:

           

TextBoxrunat="server"ID="ProductName"

           Text='<%#Eval("ProductName")%>'/>

           

RequiredFieldValidatorID="RequiredFieldValidator1"

           ControlToValidate="ProductName"

           ErrorMessage="Youmustprovidetheproduct'sname."

           runat="server">*

RequiredFieldValidator>

           

           Price:

           ___FCKpd___3lt;asp:

TextBoxrunat="server"ID="UnitPrice"

           Text='<%#Eval("UnitPrice","{0:

N2}")%>'Columns="8"/>

           

CompareValidatorID="CompareValidator1"ControlToValidate="UnitPrice"

           ErrorMessage="Theproduct'spricemustbeavalidcurrencyvalue

           greaterthanorequaltozero.Donotincludethe

           currencysymbol."

           Operator="GreaterThanEqual"Type="Currency"ValueToCompare="0"

           runat="server">*

CompareValidator>

           

Buttonrunat="server"ID="UpdateButton"Text="Update"CommandName="Update"/>

              

           

Buttonrunat="server"ID="CancelButton"Text="Cancel"CommandName="Cancel"

           CausesValidation="False"/>

           

           

            

创建EditCommand和CancelCommandEventHandlers

现在我们已经创建完ItemTemplate和EditItemTemplate,我们还需要加工eventhandlers来使ItemTemplate和EditItemTemplate之间可以切换。

象在前面讨论的一样,EditCommand事件处理里需要将EditItemIndex设为被点击Editbutton的product的index。

相反的,CancelCommand事件处理需要将EditItemIndex设为一个不存在的productindex(比如-1)。

C#

            protectedvoidProducts_EditCommand(objectsource,DataListCommandEventArgse)

           {

           Products.EditItemIndex=e.Item.ItemIndex;

           Products.DataBind();

           }

           protectedvoidProducts_CancelCommand(objectsource,DataListCommandEventArgse)

           {

           Products.EditItemIndex=-1;

           Products.DataBind();

           }

            

现在浏览该页。

开始会看到所有product的name和price显示,如图3。

点Editbutton会将选择的product展示为编辑模式。

现在Updatebutton还没有任何作用,Cancelbutton会将DataList返回到编辑前的状态,见图4。

图3:

所有Product的Name和Price被显示

图4:

点EditButton会将选择的Product的EditItemTemplate显示出来

第三步:

编辑时记下原始值

当使用开放并发控制编辑记录时,必须记下所有的编辑字段的原始值,这样用户完成编辑并点Update时,原始值可以用来和当前数据库的值进行比较以便判断其他用户是否有修改过数据。

GridView在编辑时会自动记下这些原始值。

而对DataList来说,需要我们编码来记下这些值。

GridView在postback过程里使用controlstate来记下这些原始值。

如综叙:

在DataList里编辑和删除数据  里讨论的,controlstate是和viewstate相似的用来在postback过程里记录值的方法。

controlstate不能被开发者禁用,而viewstate可以。

对DataList来说我们可以在ItemDataBoundeventhandler里手工的将原始值保存到viewstate里。

这样在postback过程里它们的值将被记住。

然后我们创建UpdateCommandeventhandler,将这些值传给BLL,它将只更新那些原始值和目前数据库值一样的那些product。

使用Page类的ViewStateproperty来写viewstate。

为ItemDataBound创建eventhandler,以便将原始值保存在viewstate里。

 

C#

            protectedvoidProducts_ItemDataBound(objectsender,DataListItemEventArgse)

           {

           if(e.Item.ItemType==ListItemType.EditItem)

           {

           //Readtheediteditem'soriginalvaluesintothePage'sviewstate

           TextBoxProductName=(TextBox)e.Item.FindControl("ProductName");

           TextBoxUnitPrice=(TextBox)e.Item.FindControl("UnitPrice");

           ViewState["original_productName"]=ProductName.Text;

           ViewState["original_unitPrice"]=UnitPrice.Text;

           }

           }

            

最开始的条件保证了仅仅只将那些被编辑的item的原始值保存下来。

然后从被编辑的product的EditItemTemplate里读出ProductName和UnitPriceTextBox的值。

FindControl("controlID")方法用来引用TextBox。

这些值被保存在viewstate变量original_productName和original_unitPrice里。

我们的DataList只有两个可编辑的输入—product的name和price—因此我们仅仅只需要两个viewstate变量。

一般来说,对DataList里每个可编辑的项,使用FindControl("controlID")引用EditItemTemplate里的控件然后将值存在viewstate变量里。

 

第四步:

将原始值和新的值传给UpdateProduct

当用户点编辑按钮来更新product时,最新的数据从数据库获得并显示在选择的product的EditItemTemplate里。

这些值被保存在两个viewstate变量里。

在用户完成修改后点更新按钮,产生postback,激发UpdateCommand。

我们需要创建一个eventhandler来将新的product的值从EditItemTemplate里读出来,并和原始值一起传到BLL。

回到实现开放式并发里,我们学习了如何创建一个天生支持开放并发的类型化DataSet,并创建了一个这样的DAL(NorthwindOptimisticConcurrency.xsd)。

在那章里我们也创建了ProductsOptimisticConcurrencyBLL类来和NorthwindOptimisticConcurrency.xsdDAL一同工作。

在DataList的例子里我们将直接通过UpdateCommand和ProductsOptimisticConcurrencyBLL交互。

ProductsOptimisticConcurrencyBLL类的UpdateProduct方法有两个参数,分别表示新值和原始值。

因此UpdateCOmmand需要读更新的值和原始值,见下面的代码:

C#

            protectedvoidProducts_UpdateCommand(objectsource,DataListCommandEventArgse)

           {

           //Makesurethevalidatorsonthepagearevalid

           if(!

Page.IsValid)

           return;

           //ReadintheProductID

           intproductID=Convert.ToInt32(Products.DataKeys[e.Item.ItemIndex]);

           //Readintheoriginalvalues

           stringoriginal_productName=null;

           decimal?

original_unitPrice=null;

           if(!

string.IsNullOrEmpty((string)ViewState["original_productName"]))

           original_productName=(string)ViewState["original_productName"];

           if(!

string.IsNullOrEmpty((string)ViewState["original_unitPrice"]))

           original_unitPrice=

           decimal.Parse((string)ViewState["original_unitPrice"],

           System.Globalization.NumberStyles.Currency);

           //Readinthenewvalues

           stringnew_productName=null;

           decimal?

new_unitPrice=null;

           TextBoxProductName=(TextBox)e.Item.FindControl("ProductName");

           TextBoxUnitPrice=(TextBox)e.Item.FindControl("UnitPrice");

           if(ProductName.Text.Trim().Length>0)

           new_productName=ProductName.Text.Trim();

           if(UnitPrice.Text.Trim().Length>0)

           new_unitPrice=

           decimal.Parse(UnitPrice.Text.Trim(),

           System.Globalization.NumberStyles.Currency);

           //CalltheUpdateProductmethodinProductsOptimisticConcurrencyBLL

           ProductsOptimisticConcurrencyBLLoptimisticProductsAPI

           =newProductsOptimisticConcurrencyBLL();

           optimisticProductsAPI.UpdateProduct(new_productName,new_unitPrice,

           productID,original_productName,original_unitPrice,productID);

           //ReturntheDataListtoitspre-editingstate

           Products.EditItemIndex=-1;

           Products.DataBind();

           }

            

UpdateCommand首先判断page是否合法。

如我们在在编辑和插入界面里添加验证控件里讨论的那样,它在处理用户输入的数据前判断Page.IsValid是否返回True。

(由于客户端验证可能因为用户的浏览器不支持或禁用了JavaScrip而被跳过)

接着,被从DataKeys集合里读出编辑的行的ProductID。

然后从ViewState集合里将原始值读到局部变量里,然后从EditItemTemplate里的TextBox里读出新的值。

然后创建ProductsOptimisticConcurrencyBLL的实例,调用UpdateProduct方法,将原始值和新值传进去。

最后,更新完后,DataList返回到编辑前状态。

完成UpdateCommand事件处理后,浏览该页。

点Chai的编辑按钮,然后将price修改为$20.00.由于你是唯一一个编辑这条记录的,因此更新会成功,你的感觉和没有使用开放并发控制的更新DataList是一样的。

打开另一个浏览器窗口并浏览OptimisticConcurrency.aspx页。

在两个页面里都点Chai的编辑按钮。

在第一个页里将product的name改为“ChaiTea”然后点更新。

这样会更新数据库并返回到DataList的编辑前状态,这时productname会显示为"ChaiTea"。

在第二个页里(它将继续保存编辑状态)将price改为$25.50,并将productname保留为“Chai”。

点击更新按钮,这时会抛出DBCo

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销 > 企业管理

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1