您现在的位置是:网站首页> 编程资料编程资料

在ASP.NET 2.0中操作数据之十七:研究插入、更新和删除的关联事件_自学过程_

2023-05-24 326人已围观

简介 在ASP.NET 2.0中操作数据之十七:研究插入、更新和删除的关联事件_自学过程_

导言

  当使用GridView、DetailsView或FormView控件的内建插入、编辑或删除特征时,在用户添加一条新记录或更新/删除一条现在记录的过程中发生了多个步骤。正如我们之前一节里所讨论的,在GridView中编辑一行时,保存(Update)和取消(Cancel)按钮将取代编辑(Edit)按钮,并且绑定列转换成TextBox。在用户更新了数据并点击保存按钮之后,下述步骤在回传时执行:

1.该GridView控件根据当前编辑行的唯一标识字段(通过DataKeyNames属性)组装它的ObjectDataSource的UpdateParameters参数,连同用户输入的值

2.该GridView控件调用它的ObjectDataSource的Update()方法,它转而调用潜在对象的适当的方法(ProductsDAL.UpdateProduct,我们之前一节里)

3.现在,这些隐含的数据,包含保存后的更改,被重新绑定到GridView控件

  在这一连串的步骤里,触发了许多事件,这让我们可以创建事件处理程序从而在需要的地方增加自定义逻辑。例如,在第1步之前,触发GridView的事件。在这里,如果有什么validation错误我们可以取消更新请求。当调用Update()方法时,触发ObjectDataSource的Updating事件,提供了增加或自定义UpdateParameters的值的机会。在ObjectDataSource的潜在对象的方法完全执行后,触发ObjectDataSource的Updated事件。针对Updated事件的事件处理程序可以检查更新操作的相关详细信息,例如影响了多少行数据,或者是否引发了一个异常。最后,在第2步之后,GridView的RowUpdated事件触发;针对此事件的事件处理程序可以检查关于刚刚完成的更新操作的相关额外信息。

  图1描述了使用GridView更新时这一系列连续的事件和步骤。图1里的这个事件模式不仅是在GridView的更新操作。从GridView、DetailsView或者FormView里插入、更新或者删除数据时,数据Web服务器控件和ObjectDataSource都会发生这一连串的pre-level和post-level的事件。

//img.jbzj.com/file_images/article/201605/2016050709475519.png

图1: 当在GridView里更新数据时,触发一连串的Pre-和Post-事件

  在这一节里,我们将探讨使用这些事件从而扩展ASP.NET数据Web服务器控件的内建插入、更新和删除功能。我们也会看看如何自定义编辑界面从而仅仅更新部分产品字段。

第一步: 更新产品的ProductName和UnitPrice字段

  在之前一节的编辑界面里,包含了产品的所有字段并且它们都不是只读的。如果我们从GridView中剔除一列(QuantityPerUnit),那么当更新时数据Web服务器控件就不会设置ObjectDataSource的QuantityPerUnit UpdateParameters的值。ObjectDataSource则传入一个null值到UpdateProduct这个业务逻辑层的方法,它将当前编辑的数据库记录的QuantityPerUnit字段更改为值。同样地,如果是一个必需的字段,例如ProductName,如果从编辑界面中剔除了,那么此更新将会失败并抛出一个“Column 'ProductName' does not allow nulls”异常。导致这一现象的原因是ObjectDataSource被配置为调用ProductsBLL类的UpdateProduct方法,它预期每一个产品字段都对应一个输入参数。因此,ObjectDataSource的UpdateParameters集合包含了该方法的每一个输入参数。

  如果我们希望提供一个允许最终用户仅仅可以更新部分字段的数据Web服务器控件,那么我们需要在ObjectDataSource的Updating事件处理程序中编程设置缺失的UpdateParameters值,或者创建并调用一个预期部分字段的BLL方法。让我们在接下来的步骤中探讨。

  特别地,让我们创建一个在一个可编辑的GridView中仅显示ProductName和UnitPrice字段的页面。这个GridView的编辑界面将仅仅允许用户更新两个显示的字段,ProductName和UnitPrice 。因为这个编辑界面仅仅提供产品的部分字段,我们需要创建一个ObjectDataSource,它使用现有的BLL的UpdateProduct方法并在Updating事件处理程序中编程设置产品的缺少的字段的值,或者我们需要创建一个新的BLL方法,它仅接受那些在GridView中已经定义的部分字段。在这一节里,我们使用后者,创建一个UpdateProduct方法的重载,它提取3个输入参数:productName、unitPrice和productID:

 [System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.Update, false)] public bool UpdateProduct(string productName, decimal? unitPrice, int productID) { Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID); if (products.Count == 0) // no matching record found, return false return false; Northwind.ProductsRow product = products[0]; product.ProductName = productName; if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; // Update the product record int rowsAffected = Adapter.Update(product); // Return true if precisely one row was updated, otherwise false return rowsAffected == 1; }

  跟原有的UpdateProduct方法类似,这个重载方法首先检查是否在数据库中存在一个指定ProductID的产品。如果不存在,它返回false,指示更新产品信息的请求失败。否则,它因而更新现存的产品记录的ProductName和UnitPrice字段并通过调用TableAdapter的Update()方法提交此更新,传入ProductsRow实例。

  通过这些对我们的ProductsBLL类的额外处理,我们现在可以创建一个简单GridView界面。打开EditInsertDelete文件夹中的DataModificationEvents.aspx ,添加一个GridView控件到页面。新建一个ObjectDataSource并配置它使用ProductsBLL类,它的Select()方法映射到GetProducts ,Update()方法映射到仅接受productName、unitPrice和productID输入参数的UpdateProduct方法重载。图2展示了映射ObjectDataSource的Update()方法到ProductsBLL类的新的UpdateProduct方法重载时的数据源配置向导。

//img.jbzj.com/file_images/article/201605/2016050709475520.png

图 2: 映射ObjectDataSource的Update()方法到新的UpdateProduct重载

  因为我们的例子将仅仅需要编辑数据的能力,不需要插入或删除记录,那么花些时间明确地指明该ObjectDataSource的Insert()和Delete()方法不会映射到ProductsBLL类的任何方法--通过到INSERT和DELETE的tab页并从下拉列表中选择(无)。

//img.jbzj.com/file_images/article/201605/2016050709475521.png

图 3: 在INSERT和DELETE的Tab页,从下拉列表中选择(无)

完成了此向导后,从GridView的职能标记里勾选上启用编辑。

完成了此数据源配置向导并绑定到GridView后,Visual Staudio就已经添加好它们的声明语法。到源视图察看该ObjectDataSource的声明标记,它将如下所示:

 [System.ComponentModel.DataObjectMethodAttribute (System.ComponentModel.DataObjectMethodType.Update, false)] public bool UpdateProduct(string productName, decimal? unitPrice, int productID) { Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID); if (products.Count == 0) // no matching record found, return false return false; Northwind.ProductsRow product = products[0]; product.ProductName = productName; if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; // Update the product record int rowsAffected = Adapter.Update(product); // Return true if precisely one row was updated, otherwise false return rowsAffected == 1; }

  因为ObjectDataSource的Insert()和Delete()方法没有映射,也就没有InsertParameters或者DeleteParameters片段。此外,因为方法映射到UpdateProduct方法重载,它仅接受3个输入参数,UpdateParameters片段也就仅包含3个Parameter实例。

  注意到ObjectDataSource的属性被设置为original_{0}。这个属性是使用数据源配置向导时Visual Studio自动设置的。因此,由于我们的BLL方法不需要传入原始的ProductID值,从ObjectDataSource的声明语法中删除所有这些属性设置。

  注意: If you如果你只是简单地在设计视图里从属性窗口清除OldValuesParameterFormatString属性的值,这个属性会依旧存在于声明语法里,但会被设置为一个空字符串。要么从声明语法里把该属性通通删掉,要么从属性窗口,设置它的值为默认值:{0} 。

虽然ObjectDataSource仅仅包含对productName、unitPrice和productID的UpdateParameters,Visual Studio还是为产品的每一个字段添加一个绑定列或CheckBoxField。

//img.jbzj.com/file_images/article/201605/2016050709475522.png

图 4: 对应产品的每一个字段,GridView都包含一个BoundField或CheckBoxField

  当最终用户编辑一个产品并点击它的保存按钮(Update),该GridView遍历那些可编辑的字段,然后把用户输入的值赋值到ObjectDataSource的UpdateParameters集合里对应的参数。如果没有对应的参数,GridView则添加一个到参数集合里。因此,如果我们的GridView包含对应产品的所有字段的绑定列或CheckBox列,那么ObjectDataSource则会调用能接受这些参数的方法重载,而不顾ObjectDataSource的声明标记指定只接受3个输入参数的事实(见图5)。类似地,如果存在某些由GridView非只读列构成的组合,而没有一个UpdateProduct重载能接受相应的参数,则会在试图保存时引发一个异常。

//img.jbzj.com/file_images/article/201605/2016050709475523.png

图 5: GridView将添加参数到ObjectDataSource的UpdateParameters集合

  为了确保ObjectDataSource调用仅接受productName、unitPrice和productID参数的UpdateProduct重载,我们需要限定GridView仅包含ProductName和UnitPrice这两个可编辑的列。这可以通过删除其他绑定列和CheckBox列,或者把这些列的ReadOnly属性设置为true实现,又或者上述两种方法结合使用。在本节里让我们简单地删除除了ProductName和UnitPrice绑定列以外的GridView的所有列,然后,GridView的声明标记将如下所示:

  即使重载预期3个输入参数,在我们的GridView里仅包含两个绑定列。这是因为productID输入参数是一个主键(primary key)的值,它通过当前编辑行的DataKeyNames属性传入值。

  我们的GridView控件,连同这个UpdateProduct重载,允许一个用户仅仅编辑产品的名称和单价而不会丢失产品的其他字段。

//img.jbzj.com/file_images/article/201605/2016050709475524.png

图 6: 仅允许编辑 ProductName和UnitPrice的界面

完善UnitPrice格式化

  虽然图6所示的GridView实例能够正常工作,UnitPrice字段还完全未被格式化,导致显示价格时缺少货币符号,还有4个小数位。为了在非编辑状态的行应用货币格式,只需要简单地设置UnitPrice绑定列的DataFormatString属性为{0:c},还有它的HtmlEncode属性为false 。

//img.jbzj.com/file_images/article/201605/2016050709475525.png

图 7: 设置UnitPrice绑定列的DataFormatString和HtmlEncode属性

通过此项更改,处于非编辑状态的行将价格格式化为货币;然而,当前编辑的行,仍然显示为没有货币符号并保留四位小数。

//img.jbzj.com/file_images/article/201605/2016050709475526.png

图 8: 现在,非编辑状态的行格式化为货币值

通过设置此绑定列的ApplyFormatInEditMode属性为true(默认为false),可以把DataFormatString属性里指定的格式化指令应用到编辑界面。

//img.jbzj.com/file_images/article/201605/2016050709475527.png

图 9: 设置此绑定列的ApplyFormatInEditMode属性为true

通过这个更改,当前编辑行里显示的UnitPrice值也被格式化为货币。

//img.jbzj.com/file_images/article/201605/2016050709475528.png

图 10: 现在,当前编辑行的值也被格式化为货币

  然而,如果把产品价格更新为文本框里带货币符号的值– 例如$19.00 – 则会抛出一个FormatException异常。当GridView尝试把用户提供的值赋值到ObjectDataSource的UpdateParameters集合,它无法把UnitPrice字符串“$19.00”转换成参数要求的decimal类型(见图11)。为了补救这个问题我们可以为GridView的RowUpdating事件添加一个事件处理程序并让它把用户输入的UnitPrice格式化为货币格式的decimal 。

  这个GridView的RowUpdating事件接受的第二个参数是一个GridViewUpdateEventArgs类型的对象,它包含一个NewValues字典,当中的每一个属性保存着用户输入的值,准备赋值到ObjectDataSource的UpdateParameters集合。我们可以重写现有的NewValues集合中的UnitPrice值为一个货币金额,通过下面的事件处理程序中的代码进行解析:

 protected void GridView1_RowUpdating(object sender, GridViewUpdateEventArgs e) { if (e.NewValues["UnitPrice"] != null) e.NewValues["UnitPrice"] = decimal.Parse(e.NewValues["UnitPrice"].ToString(), System.Globalization.NumberStyles.Currency); } 

  如果用户提供了一个诸如“$19.00”的UnitPrice值,这个值会被通过Decimal.Parse计算并解析为货币金额的值重写。这将正确的解析货币值,无论是货币符号、逗号、小数点、等等,并使用System.Globalization命名空间的NumberStyles枚举。

  图11展示因用户输入的UnitPrice值带有货币符号引起的问题,还展示了如果利用GridView的RowUpdating事件处理程序从而正确地解析这样的输入。

//img.jbzj.com/file_images/article/201605/2016050709475529.png

-六神源码网