本教程中,我们将了解怎样将业务规则集中到在表示层与 DAL 之间,充当数据交互中介的业务逻辑层 (BLL) 中。« 前一篇教程 | 下一篇教程 »
Visual Basic 教程
简介
在教程一中创建的数据访问层 (DAL) 将数据访问逻辑与表示逻辑清晰地分离开来。然而,尽管 DAL 从表示层中清晰地分离出数据访问层细节,它却并没有实施任何可能采用的业务规则。例如,我们想让我们的应用程序在Discontinued 字段设为 1 时禁止对Products 表的 CategoryID 或SupplierID字段的修改,还有,我们可能想实施一些资历规则以便禁止发生这样的情况:雇员被其后入职的另一雇员所管理。另一种常见的情形是授权 – 可能只有处于特定职位的用户可以删除产品或更改UnitPrice 值。
通过本教程,我们可以了解怎样将业务规则集中到在表示层与 DAL 之间充当数据交互中介的业务逻辑层 (BLL) 中。在真实的应用程序中,BLL 应作为一个单独的类库项目而实现。然而,为了简化项目结构,在这些教程中,我们以App_Code 文件夹下的一系列的类来实现 BLL。图 1 展示了表示层、BLL 和 DAL 之间的结构关系。
概述
•创建数据访问层•创建业务逻辑层•母版页与网站导航
基础报表
•使用
ObjectDataSource显示数据•声明式参数•通过编码设定
ObjectDataSource参数值主/明细报表
•使用
DropDownList的主/明细筛选•使用两个
DropDownList的主/明细筛选
•跨两页面的主/明细筛选•使用可选的GridView和DetailView 控件 创建主/明细报表
自定义格式报表
•基于数据自定义格式•在GridView控件中使用
TemplateField•在DetailsView控件中使用
TemplateField•使用 FormView的模板•在GridView的脚注中显示小结信息
图1: BLL 将表示层与数据访问层分隔开来并且实施业务规则。我们不采用创建单独类实现 业务逻辑
[ http://en.wikipedia.org/wiki/Business_logic ] 的方法,而是选择将该逻辑直接放在有部分类的 Typed DataSet中。有关创建和扩展 Typed DataSet 的示例,请向前参考教程一。
编辑,插入和删除数据
•数据插入、更新和删除概述
步骤 1:创建 BLL 类
我们的 BLL将由四个类组成,分别对应 DAL中不同的 TableAdapter.。每个BLL类都具有一些方法,这些方法可以从DAL 中该类对应的TableAdapter中检索、插入、更新或删除数据并应用相应的业务规则。
http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20
创建业务逻辑层VBPage 2of 16
•探讨与插入、更新、删除相关的事件•在ASP.NET页面中处理BLL与DAL级别的异 常
•为编辑与插入界面添加验证控件•自定义数据编辑界面•实施并发优化
•删除时添加客户端确认•基于用户来数据修改功能为了更清楚地区分 DAL 的相关类与 BLL 的相关类,我们在 App_Code文件夹下创建两个子文件夹:DAL 和 BLL。创建时,只需右健单击 Solution Explorer 中的
App_Code 文件夹并选择 New Folder。创建了这两个文件夹后,将教程一中创建的Typed DataSet 移动到 DAL 子文件夹中。
然后,在 BLL 子文件夹中创建四个 BLL 类文件。为此,右键单击 BLL 子文件夹,选择 Add a New Item,然后选择 Class 模板。将这四个类分别命名为ProductsBLL, CategoriesBLL,SuppliersBLL和EmployeesBLL。
分页和排序
•报表数据的分页与排序•对大数据量数据的高效分页•对自定义分页数据进行排序•创建自定义排序用户界面
图2: 在 App_Code文件夹中添加四个新类
接下来让我们在每个类中添加一些方法,这些方法只是简单地封装教程一中为
TableAdapters 定义的方法。目前,这些方法只是对 DAL 中内容的直接调用,稍后我们会返回到这些方法中来添加任何所需的业务逻辑。
注意:如果您当前使用的是 Visual Studio Standard Edition 或以上版本(即,当前使用的不是 Visual Web Developer),您可以使用 Class Designer
[ http://msdn.microsoft.com/library/default.asp.aspx?url=/library/en-us/dv_vstechart/html/clssdsgnr.asp ] ,以可视的方式随意设计自己的类。有关 Visual Studio 中该新特性的详细信息,请参见 Class Designer Blog [ http://blogs.msdn.com/classdesigner/default.aspx ] 。对于 ProductsBLL类,总共需要添加七个方法:
•GetProducts() – 返回所有产品
•GetProductByProductID(productID) – 返回具有指定产品 ID 的产品•GetProductsByCategoryID(categoryID)– 返回指定种类中的所有产品•GetProductsBySupplier(supplierID) – 返回来自指定供应商的所有产品。•AddProduct(productName, supplierID, categoryID,
quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) – 通过传入值将一个新产品插入到数据库中;返回新插入记录的ProductID 值
•UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) – 通过传入值更新数据库中的一个现有产品;如果正好更新了一行则返回为True,否则返回为 False
•DeleteProduct(productID) – 从数据库中删除指定产品
ProductsBLL.vb
复制代码
Imports NorthwindTableAdapters
Public Class ProductsBLL 自定义按钮事件 •添加和响应 GridView 控件的按钮 使用DataList和 Repeater控件显示数据 •使用 DataList 与 Repeater 控件显示 数据 •根据数据格式化 DataList 控件和 Repeater 控件•使用 DataList 控件每行显示多个记录•嵌套的Web数据控件 使用DataList和 Repeater进行数据筛选 •使用 DropDownList的主/明细筛选 •跨两页面的主/明细筛选•使用DataList 和Bulleted List创建主/ 明细报表 通过DataList编辑和删除数据 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 3of 16 •在 DataList 中进行数据编辑与删除操 作概述 •执行批处理更新•处理BLL层和DAL层异常•为 DataList 控件的编辑界面添加验证 控件•定制 DataList 的编辑界面•实现并发优化 •删除时添加客户端确认•基于用户用户修改功能 使用DataList和 Repeater控件进行分页和排序 •在 DataList 或 Repeater 控件中分页 报表数据•在 DataList 或 Repeater 控件中对数 据进行排序 在 DataList 和 Repeater 控件中定制按钮事件 •在 DataList 和 Repeater 控件中定制 按钮事件 从ASP.NET页面直接访问数据库 •使用 SqlDataSource控件查询数据•使用 SqlDataSource进行带参数的查询•使用 SqlDataSource 插入、更新和删除 数据•使用 SqlDataSource 实现并发优化扩展GridView控件 •为 GridView 添加一列单选按钮•在 GridView 控件中添加一列复选框•从 GridView 控件的脚注中添加一个新 记录 Private _productsAdapter As ProductsTableAdapter = Nothing Protected ReadOnly Property Adapter() As ProductsTableAdapter Get If _productsAdapter Is Nothing Then _productsAdapter = New ProductsTableAdapter() End If Return _productsAdapter End Get End Property Public Function GetProducts() As Northwind.ProductsDataTable Return Adapter.GetProducts() End Function Public Function GetProductByProductID(ByVal productID As Integer) _ As Northwind.ProductsDataTable Return Adapter.GetProductByProductID(productID) End Function Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _ As Northwind.ProductsDataTable Return Adapter.GetProductsByCategoryID(categoryID) End Function Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _ As Northwind.ProductsDataTable Return Adapter.GetProductsBySupplierID(supplierID) http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 4of 16 操作二进制文件 •上载文件 •在 Web 数据控件中显示二进制数据•添加新记录时包含文件上载选项•更新与删除现有二进制数据数据缓存 •使用 ObjectDataSource缓存数据•在架构中缓存数据•应用程序启动时缓存数据•使用 SQL 缓存依赖项 基于数据库的站点地图 •创建定制的数据库驱动的站点地图提供 程序 批量数据处理 •在事务中封装数据库修改•批量更新•批量删除•批量插入 高级数据库访问操作 •为强类型 DataSet 的 TableAdapter 创 建新的存储过程•对强类型 DataSet 的 TableAdapter 使 用现有存储过程•更新 TableAdapter 来使用 JOIN•添加更多的 DataTable 列•使用计算列•配置数据访问层的连接级和命令级设置•保护连接字符串和其他配置信息•调试存储过程 •使用托管代码创建存储过程和用户自定 义函数 End Function Public Function AddProduct( _ productName As String, supplierID As Nullable(Of Integer), _ categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean) _ As Boolean Dim products As New Northwind.ProductsDataTable() Dim product As Northwind.ProductsRow = products.NewProductsRow() product.ProductName = productName If Not supplierID.HasValue Then product.SetSupplierIDNull() Else product.SupplierID = supplierID.Value End If If Not categoryID.HasValue Then product.SetCategoryIDNull() Else product.CategoryID = categoryID.Value End If If quantityPerUnit Is Nothing Then product.SetQuantityPerUnitNull() Else product.QuantityPerUnit = quantityPerUnit End If If Not unitPrice.HasValue Then product.SetUnitPriceNull() http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 5of 16 Else product.UnitPrice = unitPrice.Value End If If Not unitsInStock.HasValue Then product.SetUnitsInStockNull() Else product.UnitsInStock = unitsInStock.Value End If If Not unitsOnOrder.HasValue Then product.SetUnitsOnOrderNull() Else product.UnitsOnOrder = unitsOnOrder.Value End If If Not reorderLevel.HasValue Then product.SetReorderLevelNull() Else product.ReorderLevel = reorderLevel.Value End If product.Discontinued = discontinued products.AddProductsRow(product) Dim rowsAffected As Integer = Adapter.Update(products) Return rowsAffected = 1 End Function Public Function UpdateProduct(_ productName As String, supplierID As Nullable(Of Integer), _ categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 6of 16 unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean, productID As Integer) _ As Boolean Dim products As Northwind.ProductsDataTable = _ Adapter.GetProductByProductID(productID) If products.Count = 0 Then Return False End If Dim product as Northwind.ProductsRow = products(0) product.ProductName = productName If Not supplierID.HasValue Then product.SetSupplierIDNull() Else product.SupplierID = supplierID.Value End If If Not categoryID.HasValue Then product.SetCategoryIDNull() Else product.CategoryID = categoryID.Value End If If quantityPerUnit Is Nothing Then product.SetQuantityPerUnitNull() Else product.QuantityPerUnit = quantityPerUnit End If If Not unitPrice.HasValue Then product.SetUnitPriceNull() Else product.UnitPrice = unitPrice.Value End If If Not unitsInStock.HasValue Then http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 7of 16 product.SetUnitsInStockNull() Else product.UnitsInStock = unitsInStock.Value End If If Not unitsOnOrder.HasValue Then product.SetUnitsOnOrderNull() Else product.UnitsOnOrder = unitsOnOrder.Value End If If Not reorderLevel.HasValue Then product.SetReorderLevelNull() Else product.ReorderLevel = reorderLevel.Value End If product.Discontinued = discontinued Dim rowsAffected As Integer = Adapter.Update(product) Return rowsAffected = 1 End Function Public Function DeleteProduct(ByVal productID As Integer) As Boolean Dim rowsAffected As Integer = Adapter.Delete(productID) Return rowsAffected = 1 End Function End Class 这些方法 – GetProducts,GetProductByProductID,GetProductsByCategoryID 和GetProductBySuppliersID – ,只是返回数据,它们相当直接简单,因为它们只是向下调用 DAL 中的内容。在一些场合下,可能会有一些业务规则需要在此层实现(例如基于当前已登录用户或用户所处职位的授权规则,可以访问不同的数据),但在这里我们只是保留这些方法不变。因此,对于这些方法,BLL 只是充当了一个代理的作用,表示层通过这个代理来访问数据访问层中的底层数据。 AddProduct 和 UpdateProduct方法将产品各字段的值以参数形式传入,它们的作用分别是: 添加一个新产品,更新一个现有产品。由于Product 表的许多列,如 CategoryID、SupplierID 和 UnitPrice, 都可接受 NULL值,AddProduct 和UpdateProduct中与这样的列相对应的输入参数使用 Nullable 类型 [ http://msdn.microsoft.com/vcsharp/2005/overview/language/nullabletypes/ ] 。 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 8of 16 Nullable 类型对于 .NET 2.0 来说是新类型,利用该类型所提供的技术,我们可以指示一个值类型是否可以是空类型。有关Nullable [ http://msdn2.microsoft.com/en-US/library/b3h38hb0(VS.80).aspx ] 结构的更多信息,请参考 Paul Vick [ http://www.panopticoncentral.net/default.aspx ] 的博客文章The Truth About Nullable Types and VB [ http://www.panopticoncentral.net/archive/2004/06/04/1180.aspx ] 和技术文档。 这三个方法均返回布尔值,该值指示是否成功的插入、更新或删除了一行。返回该值的原因是, 方法的操作并不一定会影响到一行。例如,如果页面开发人员调用 DeleteProduct 时传入的ProductID 并非一个现有产品的 ID,则发给数据库的 DELETE语句不会产生任何影响, 因而DeleteProduct 方法会返回False。 请注意,当添加一个新产品或更新一个现有产品时,我们将新的或更改的产品的字段值当作一组数值传入, 而不是为此接受一个 ProductsRow实例。选择该方式的原因是,ProductsRow类派生于 ADO.NET DataRow 类, 而后者并没有一个默认的无参构造函数。为了创建一个新的ProductsRow 实例,首先要创建一个 ProductsDataTable 实例,然后调用它的NewProductRow() 方法(就像我们在AddProduct 方法中作的那样)。 当我们使用 ObjectDataSource 插入或更新产品时,其缺陷就会暴露出来。简言之, ObjectDataSource 会尝试为输入参数创建一个实例。如果 BLL 方法期待的是一个 ProductsRow 实例, 则 ObjectDataSource 会尝试创建一个这样的实例,但是,由于缺少默认的无参数构造函数,该尝试失败。 有关该问题的详细信息,请参见以下两个 ASP.NET 论坛: Updating ObjectDataSources with Strongly-Typed DataSets [ http://forums.asp.net/1098630/ShowPost.aspx ] 和 Problem With ObjectDataSource and Strongly-Typed DataSet [ http://forums.asp.net/1048212/ShowPost.aspx ] 。 另外,AddProduct 和UpdateProduct 中的代码都会创建一个ProductsRow实例并以刚传入的值对该实例进行赋值。当向 DataRow 的一些 DataColumn 赋值时, 可发生各种字段级的验证检查。因此,将传入的值进行人工的验证有助于确保传递给 BLL 方法的数据的有效性。不幸的是,Visual Studio 生成的强类型的 DataRow 类并不使用 nullable 类型。 而为了给 DataRow 中的特定 DataColumn赋数据库空值,我们必须使用SetColumnNameNull() 方法。 在 UpdateProduct 中,我们首先用GetProductByProductID(productID)载入要更新的产品。尽管这看似是一次不必要的对数据库的操作,在将来的介绍并行优化的教程中,该往返将会被证明是值得的。并行优化技术可确保两个同时对同一数据进行操作的用户不会在不经意间覆盖彼此所作的更改。获取整个记录还使以下事情变得容易:在 BLL 中创建更新方法,使该方法只修改DataRow 的所有列的一个子集。当我们研究SuppliersBLL类时,我们会看到这样一个例子。最后,请注意对 ProductsBLL 类使用了DataObject 属性 [ http://msdn2.microsoft.com/en-us/library/system.componentmodel.dataobjectattribute.aspx ] (接近文件开头,类声明语句前面的[System.ComponentModel.DataObject]标签),而其方法有 DataObjectMethodAttribute 属性 [ http://msdn2.microsoft.com/en-us/library/system.componentmodel.dataobjectmethodattribute.aspx ] 。DataObject属性将该类标记为一个适合绑定到 ObjectDataSource control [ http://msdn2.microsoft.com/en-us/library/9a4kyhcx.aspx ] 的对象,而DataObjectMethodAttribute属性则指示该方法的用途。在将来的教程中可以看到,ASP.NET 2.0 的 ObjectDataSource 使得以声明的方式从类中访问数据变得容易。默认情况下,在 ObjectDataSource 向导的下拉列表中只显示出标记为DataObject的那些类,这样有助于在该向导中筛选出可绑定到的那些类。 ProductsBLL类没有这些属性一样会工作良好,但是,加入这些属性可以使得在ObjectDataSource向导下的工作更为轻松。 添加其它类 在完成 ProductsBLL类的编写后,我们还需要添加一些处理种类、供应商及雇员数据的类。我们花一些时间用上面例子中的概念来创建下面的类和方法: •CategoriesBLL.cs •GetCategories() •GetCategoryByCategoryID(categoryID)•SuppliersBLL.cs •GetSuppliers() •GetSupplierBySupplierID(supplierID)•GetSuppliersByCountry(country) •UpdateSupplierAddress(supplierID,address, city,country) •EmployeesBLL.cs •GetEmployees() •GetEmployeeByEmployeeID(employeeID)•GetEmployeesByManager(managerID) http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 9of 16 值得注意的一个方法是 SuppliersBLL类的 UpdateSupplierAddress 方法。 该方法提供了一个接口以便只更新供应商的地址信息。在内部实现上,该方法读取指定 supplierID 的 SupplierDataRow对象(使用 GetSupplierBySupplierID来读取), 设置其相关地址属性,然后向下调用SupplierDataTable的更新方法。UpdateSupplierAddress方法如下: 复制代码 Public Function UpdateSupplierAddress(ByVal supplierID As Integer, _ ByVal address As String, ByVal city As String, ByVal country As String) _ As Boolean Dim suppliers As Northwind.SuppliersDataTable = _ Adapter.GetSupplierBySupplierID(supplierID) If suppliers.Count = 0 Then Return False Else Dim supplier As Northwind.SuppliersRow = suppliers(0) If address Is Nothing Then supplier.SetAddressNull() Else supplier.Address = address End If If city Is Nothing Then supplier.SetCityNull() Else supplier.City = city End If If country Is Nothing Then supplier.SetCountryNull() Else supplier.Country = country End If Dim rowsAffected As Integer = Adapter.Update(supplier) Return rowsAffected = 1 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 10of 16 End If End Function 步骤 2:通过 BLL 类访问 Typed DataSets 在教程一中我们看到了直接使用 Typed DataSet 的编程例子。而现在我们已添加了一些 BLL 类,因此表示层应转而基于 BLL 而工作。教程一的 AllProducts.aspx 例子使用了 ProductsTableAdapter 来将产品列表绑定到一个 GridView,见下面的代码: 复制代码 Dim productsAdapter As New ProductsTableAdapter() GridView1.DataSource = productsAdapter.GetProducts() GridView1.DataBind() 要使用 BLL 类,只需改变代码的第一行 – 只需用 ProductBLL 对象代替 ProductsTableAdapter 对象: 复制代码 Dim productLogic As New ProductsBLL() GridView1.DataSource = productLogic.GetProducts() GridView1.DataBind() 也可以使用 ObjectDataSource 以声明的方式来访问 BLL 类(如同 Typed DataSet)。我们将在后续教程中更为详细地讨论 ObjectDataSource。 图3: 产品列表显示于 GridView 中 步骤 3:向 DataRow 类添加字段级验证 字段级验证是进行插入或更新操作时针对业务对象的属性值而进行的检查。下面是对产品的一些字段级验证规则: •ProductName 字段的长度不能超过 40 个字符•QuantityPerUnit 字段的长度不能超过 20 个字符 •ProductID, ProductName 和Discontinued字段是必需的,但所有其它字段是可选的•UnitPrice, UnitsInStock,UnitsOnOrder, 和 ReorderLevel字段必须大于等于零 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 11of 16 T这些规则可以并且应该在数据库级表达出来。Products 表的相应列的数据类型可反 映对 ProductName 和 QuantityPerUnit 字段的字符数(分别为 nvarchar(40) 和 nvarchar(20))。对字段是可选还是必需的表达是这样的:数据库表列允许还是不允许 NULL。四个 check constraints [ http://msdn2.microsoft.com/en-us/library/ms188258.aspx ] 的存在确保只有大于等于零的值才可赋值给UnitPrice、UnitsInStock、UnitsOnOrder 和 ReorderLevel 列。 这些规则除了在数据库级实施外还应在 DataSet 级实施。事实上,域长度,以及某值是必需的还是可选的,已被 DataTable 的 DataColumn 集所捕获。要查看现有的自动提供的域级验证,可转到 DataSet Designer,从其中一个 DataTable 中选择一个域,然后转至 Properties 窗口。如图 4 所示,ProductsDataTable 中的 QuantityPerUnit DataColumn 允许的最大长度是 20 个字符,并且允许 NULL 值。如果我们试图将 ProductsDataRow 的 QuantityPerUnit 属性设置为一个超过 20 个字符的字符串值,系统会抛出 ArgumentException异常。 图4: DataColumn 提供基本域级验证 不幸的是,我们不能通过 Properties 窗口指定边界检查,如,UnitPrice 必须大于等于零这样的检查。为了提供此类字段级验证,需要创建一个针对 DataTable 的 ColumnChanging [ http://msdn2.microsoft.com/en-us/library/system.data.datatable.columnchanging(VS.80).aspx ] 事件的Event Handler。如 前一教程所述,Typed DataSet 创建的 DataSet、DataTables 和 DataRow 对象可以通过使用部分类来扩展。利用该技术我们可以为 ProductsDataTable 类创建一个 ColumnChanging Event Handler。首先,在 App_Code 文件夹下创建一个名为 ProductsDataTable.ColumnChanging.vb 的类。 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 12of 16 图5: 在App_Code 文件夹中添加一个新类 其次,创建一个针对 ColumnChanging 事件的Event Handler以确保 UnitPrice、UnitsInStock、UnitsOnOrder 和 ReorderLevel 列的值(如果不是 NULL)大于等于零。其中任何一列超出范围,系统都会给出 ArgumentException。ProductsDataTable.ColumnChanging.vb 复制代码 Imports System.data Partial Public Class Northwind Partial Public Class ProductsDataTable Public Overrides Sub BeginInit() AddHandler Me.ColumnChanging, AddressOf ValidateColumn End Sub Sub ValidateColumn(sender As Object, e As DataColumnChangeEventArgs) If e.Column.Equals(Me.UnitPriceColumn) Then If Not Convert.IsDBNull(e.ProposedValue) AndAlso _ CType(e.ProposedValue, Decimal) < 0 Then Throw New ArgumentException( _ \"UnitPrice cannot be less than zero\\"UnitPrice\") End If ElseIf e.Column.Equals(Me.UnitsInStockColumn) OrElse _ e.Column.Equals(Me.UnitsOnOrderColumn) OrElse _ e.Column.Equals(Me.ReorderLevelColumn) Then If Not Convert.IsDBNull(e.ProposedValue) AndAlso _ CType(e.ProposedValue, Short) < 0 Then Throw New ArgumentException(String.Format( _ \"{0} cannot be less than zero\e.Column.ColumnName), _ http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 13of 16 e.Column.ColumnName) End If End If End Sub End Class End Class 步骤 4:向 BLL 类添加定制的业务规则 除了字段级验证外,可能还有高级定制的业务规则,这些规则涉及不同的实体,或者涉及到不能在单个列中表达的概念,例如: •如果一产品为断货 (discontinued) 产品,其 UnitPrice 就不能被更新。•雇员的居住国必须与其经理的居住国相同。 •如果某产品是其供应商提供的唯一产品,该产品就不能为断货产品。 BLL 类应含有检查,以确保遵守应用程序的业务规则。可将这些检查直接添加到它们所应用到的方法中。 假设我们的业务规则规定:如果某产品是指定供应商的唯一产品,该产品就不能标记为 discontinued。即,如果产品 X 是我们从供应商 Y 处购买的唯一产品,我们就不能将 X 标记为 discontinued;但是如果供应商 Y 为我们提供了三个产品:A、B 和 C,那么我们可将其中任何一个或所有的标记为 discontinued。这是一个奇怪的业务规则,但业务规则并不总是符合一般常识! 为了对 UpdateProducts 方法实施此业务规则,我们首先检查 Discontinued 是否设置为 True,如是,我们会调用 GetProductsBySupplierID 来确定我们从该产品的供应商处购买了多少个产品。如果从该供应商处只购买了一个产品,我们抛出 ApplicationException异常。 复制代码 Public Function UpdateProduct( _ productName As String, supplierID As Nullable(Of Integer), _ categoryID As Nullable(Of Integer), quantityPerUnit As String, _ unitPrice As Nullable(Of Decimal), unitsInStock As Nullable(Of Short), _ unitsOnOrder As Nullable(Of Short), reorderLevel As Nullable(Of Short), _ discontinued As Boolean, productID As Integer) _ As Boolean Dim products As Northwind.ProductsDataTable = _ Adapter.GetProductByProductID(productID) If products.Count = 0 Then Return False End If Dim product As Northwind.ProductsRow = products(0) http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 14of 16 If discontinued Then Dim productsBySupplier As Northwind.ProductsDataTable = _ Adapter.GetProductsBySupplierID(product.SupplierID) If productsBySupplier.Count = 1 Then Throw New ApplicationException( _ \"You cannot mark a product as discontinued if it is \" & _ \"the only product purchased from a supplier\") End If End If product.ProductName = productName If Not supplierID.HasValue Then product.SetSupplierIDNull() Else product.SupplierID = supplierID.Value End If If Not categoryID.HasValue Then product.SetCategoryIDNull() Else product.CategoryID = categoryID.Value End If If quantityPerUnit Is Nothing Then product.SetQuantityPerUnitNull() Else product.QuantityPerUnit = quantityPerUnit End If If Not unitPrice.HasValue Then product.SetUnitPriceNull() Else product.UnitPrice = unitPrice.Value End If If Not unitsInStock.HasValue Then product.SetUnitsInStockNull() http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 15of 16 Else product.UnitsInStock = unitsInStock.Value End If If Not unitsOnOrder.HasValue Then product.SetUnitsOnOrderNull() Else product.UnitsOnOrder = unitsOnOrder.Value End If If Not reorderLevel.HasValue Then product.SetReorderLevelNull() Else product.ReorderLevel = reorderLevel.Value End If product.Discontinued = discontinued Dim rowsAffected As Integer = Adapter.Update(product) Return rowsAffected = 1 End Function 在表示层对验证错误进行响应 从表示层调用 BLL 时,我们可以决定是尝试对任何可能出现的异常情况进行处理,还是让这些异常直接抛给 ASP.NET(它们会引发HttpApplication 的错误事件)。要在编程使用 BLL时处理一个异常,我们可以使用 Try...Catch [ http://msdn2.microsoft.com/en-us/library/fk6t46tz(VS.80).aspx ] 块,如下所示: 复制代码 Dim productLogic As New ProductsBLL() Try productLogic.UpdateProduct(\"Scotts Tea\ -14, 10, Nothing, Nothing, False, 1) Catch ae As ArgumentException Response.Write(\"There was a problem: \" & ae.Message) End Try 在以后的教程中我们会看到,当使用一个 Web 数据控件来插入、更新或删除数据 时,可以通过一个Event Handler对从 BLL抛出的异常进行处理而不用将该处理代码封装于 Try...Catch 块中。 http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx2010-02-20 创建业务逻辑层VBPage 16of 16 小结 一个结构良好的应用程序都有清晰的层次结构,每层都封装有特定的任务。在本系列文章的第一篇教程中,我们用 Typed DataSets 创建了一个数据访问层;在本教程中,我们建立了一个业务逻辑层,该层包括我们的应用程序的 App_Code 文件夹下的一系列类,这些类向下调用 DAL 中的内容。我们的应用程序通过 BLL 实现了字段级和业务级逻辑。在本教程中,我们创建了一个的 BLL,除此之外的另一个选择是,利用部分类来扩展 TableAdapters 的方法。但是,使用这一技术,我们并不能重写现有的方法,也不能象本文中采用的方式一样清晰地分隔开我们的 DAL 和 BLL。完成 DAL 和 BLL 的代码编写后,我们就可以着手编写我们的表示层代码了。在下一教程 中,我们会短暂地偏离数据访问主题,转而去定义一个将为所有教程所使用的一致的页面布局。 快乐编程! http://msdn.microsoft.com/zh-cn/dd179324(printer).aspx下一篇教程 2010-02-20 因篇幅问题不能全部显示,请点此查看更多更全内容
Copyright © 2019- jqkq.cn 版权所有 赣ICP备2024042794号-4
违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com
本站由北京市万商天勤律师事务所王兴未律师提供法律服务