ASP.NET2.0数据操作之创建业务逻辑层(2)
点击次数:17 次 发布日期:2008-11-26 12:54:55 作者:源代码网
|
源代码网推荐 源代码网推荐 完成了ProductsBLL类之后,我们还要添加一些为categories、suppliers和employees服务的类。让我们花点时间来创建下面的类,根据上面的例子来做就是了: 源代码网推荐 源代码网推荐 · CategoriesBLL.cs 源代码网推荐 源代码网推荐 o GetCategories() 源代码网推荐 源代码网推荐 o GetCategoryByCategoryID(categoryID) 源代码网推荐 源代码网推荐 · SuppliersBLL.cs 源代码网推荐 源代码网推荐 o GetSuppliers() 源代码网推荐 源代码网推荐 o GetSupplierBySupplierID(supplierID) 源代码网推荐 源代码网推荐 o GetSuppliersByCountry(country) 源代码网推荐 源代码网推荐 o UpdateSupplierAddress(supplierID, address, city, country) 源代码网推荐 源代码网推荐 · EmployeesBLL.cs 源代码网推荐 源代码网推荐 o GetEmployees() 源代码网推荐 源代码网推荐 o GetEmployeeByEmployeeID(employeeID) 源代码网推荐 源代码网推荐 o GetEmployeesByManager(managerID) 源代码网推荐 源代码网推荐 SuppliersBLL类中的UpdateSupplierAddress方法是一个值得注意的东西。这个方法提供了一个仅仅更新供应商地址信息的接口。它首先根据指定的SupplierID读出一个SupplierDataRow(使用GetSupplierBySupplierID方法),设置其关于地址的所有属性,然后调用SupplierDataTable的Update方法。UpdateSupplierAddress方法的代码如下所示: 源代码网推荐 源代码网推荐 UpdateSupplierAddress 源代码网推荐 源代码网推荐 1[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)] 源代码网推荐 2public bool UpdateSupplierAddress(int supplierID, string address, string city, string country) 源代码网推荐 3{ 源代码网推荐 4 Northwind.SuppliersDataTable suppliers = Adapter.GetSupplierBySupplierID(supplierID); 源代码网推荐 5 if (suppliers.Count == 0) 源代码网推荐 6 // 没有找到匹配的项,返回false 源代码网推荐 7 return false; 源代码网推荐 8 else 源代码网推荐 9 { 源代码网推荐 10 Northwind.SuppliersRow supplier = suppliers[0]; 源代码网推荐 11 源代码网推荐 12 if (address == null) supplier.SetAddressNull(); else supplier.Address = address; 源代码网推荐 13 if (city == null) supplier.SetCityNull(); else supplier.City = city; 源代码网推荐 14 if (country == null) supplier.SetCountryNull(); else supplier.Country = country; 源代码网推荐 15 源代码网推荐 16 // 更新供应商的关于地址的信息 源代码网推荐 17 int rowsAffected = Adapter.Update(supplier); 源代码网推荐 18 源代码网推荐 19 // 如果刚好更新了一条记录,则返回true,否则返回false 源代码网推荐 20 return rowsAffected == 1; 源代码网推荐 21 } 源代码网推荐 22} 源代码网推荐 23 源代码网推荐 源代码网推荐 可以从页面顶部的链接处下载BLL类的完整代码。 源代码网推荐 源代码网推荐 第二步:通过BLL类访问类型化数据集 源代码网推荐 源代码网推荐 在本教程的第一节中,我们给出了直接使用类型化数据集的例子,不过在我们添加了BLL类之后,表示层就可以通过BLL来工作了。在本教程的第一节中的AllProducts.ASPx的例子中,ProductsTableAdapter用于将产品列表绑定到GridView上,代码如下所示: 源代码网推荐 源代码网推荐 1 ProductsTableAdapter productsAdapter = new ProductsTableAdapter(); 源代码网推荐 2 GridView1.DataSource = productsAdapter.GetProducts(); 源代码网推荐 3 GridView1.DataBind(); 源代码网推荐 源代码网推荐 要使用新的BLL类,我们所需要做的仅仅是简单的修改一下第一行代码。用ProductBLL对象来代替 ProductsTableAdapter即可: 源代码网推荐 源代码网推荐 1 ProductsBLL productLogic = new ProductsBLL(); 源代码网推荐 2 GridView1.DataSource = productLogic.GetProducts(); 源代码网推荐 3 GridView1.DataBind(); 源代码网推荐 源代码网推荐 BLL类也可以通过使用ObjectDataSource来清晰明了的访问(就像类型化数据集一样)。我们将在接下来的教程中详细的讨论ObjectDataSource。 源代码网推荐 源代码网推荐
源代码网推荐 图三:GridView中显示的产品列表 源代码网推荐 源代码网推荐 第三步:给DataRow添加字段级验证 源代码网推荐 源代码网推荐 字段级验证是指在插入或更新时检查业务对象所涉及到的所有属性值。拿产品来举个例,某些字段级的验证规则如下所示: 源代码网推荐 源代码网推荐 · ProductName字段不得超过40个字符 源代码网推荐 源代码网推荐 · QuantityPerUnit字段不得超过20个字符 源代码网推荐 源代码网推荐 · ProductID、ProductName以及Discontinued字段是必填的,而其他字段则是可填可不填的 源代码网推荐 源代码网推荐 · UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段不得小于0 源代码网推荐 源代码网推荐 这些规则可以或者说是应该在数据库层被描述出来。ProductName和QuantityPerUnit字段上的字符数限制可以通过Products表中相应列的数据类型来实现(分别为nvarchar(40) and nvarchar(20))。字段“是否必填”可以通过将数据库中表的相应列设置为“允许为NULL”来实现。为了保证UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段的值不小于0,可以分别在它们的相应列上加一个约束。 源代码网推荐 源代码网推荐 除了在数据库中应用了这些规则之外,它们同时也将被其应用在DataSet上。事实上,字段长度和是否允许为空等信息已经被应用到了各DataTable的DataColumn集合中。我们可以在数据集设计器(DataSet Designer)中看到已经存在的字段级验证,从某个DataTable中选择一个字段,然后在属性窗口中就可以找到了。如图四所示,ProductDataTable中的QuantityPerUnit字段允许空值并且最大长度为20各字符。如果我们试图给某个ProductsDataRow的QuantityPerUnit属性设置一个长度大于20个字符的字符串,将会有一个ArgumentException被抛出。 源代码网推荐
源代码网推荐 图四:DataColumn提供了基本的字段级验证 源代码网推荐 源代码网推荐 不幸的是,我们不能通过属性窗口指定一个边界检查,比如UnitPrice的值不能小于0。为了提供这样的字段级验证,我们需要为DataTable的ColumnChanging事件建立一个Event Handler。正如上一节教程中所提到的那样,由类型化数据集创建的DataSet、DataTable还有DataRow对象可以通过partial类来进行扩展。使用这个技术,我们可以为ProductDataTable创建一个ColumnChanging的Event Handler。我们先在App_Code文件夹中新建一个名为ProductsDataTable.ColumnChanging.cs的类文件,如下图所示。 源代码网推荐 源代码网推荐
源代码网推荐 图五:在App_Code文件夹中添加新类 源代码网推荐 源代码网推荐 然后,给ColumnChanging事件创建一个Event handler,以保证UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段的值不小于0。如果这些列的值超出范围就抛出一个ArgumentException。 源代码网推荐 源代码网推荐 ProductsDataTable.ColumnChanging.cs 源代码网推荐 源代码网推荐 1public partial class Northwind 源代码网推荐 2{ 源代码网推荐 3 public partial class ProductsDataTable 源代码网推荐 4 { 源代码网推荐 5 public override void BeginInit() 源代码网推荐 6 { 源代码网推荐 7 this.ColumnChanging += ValidateColumn; 源代码网推荐 8 } 源代码网推荐 9 源代码网推荐 10 void ValidateColumn(object sender, DataColumnChangeEventArgs e) 源代码网推荐 11 { 源代码网推荐 12 if(e.Column.Equals(this.UnitPriceColumn)) 源代码网推荐 13 { 源代码网推荐 14 if(!Convert.IsDBNull(e.ProposedValue) && (decimal)e.ProposedValue < 0) 源代码网推荐 15 { 源代码网推荐 16 throw new ArgumentException("UnitPrice cannot be less than zero", "UnitPrice"); 源代码网推荐 17 } 源代码网推荐 18 } 源代码网推荐 19 else if (e.Column.Equals(this.UnitsInStockColumn) || 源代码网推荐 20 e.Column.Equals(this.UnitsOnOrderColumn) || 源代码网推荐 21 e.Column.Equals(this.ReorderLevelColumn)) 源代码网推荐 22 { 源代码网推荐 23 if (!Convert.IsDBNull(e.ProposedValue) && (short)e.ProposedValue < 0) 源代码网推荐 24 { 源代码网推荐 25 throw new ArgumentException(string.Format("{0} cannot be less than zero", e.Column.ColumnName), e.Column.ColumnName); 源代码网推荐 26 } 源代码网推荐 27 } 源代码网推荐 28 } 源代码网推荐 29 } 源代码网推荐 30} 源代码网推荐 源代码网推荐 第四步:给BLL类添加业务规则 源代码网推荐 源代码网推荐 除了字段级的验证,可能还有一些不能在单个列中表示的包含不同实体或概念的更高级的业务规则,比如: 源代码网推荐 源代码网推荐 · 如果一个产品被标记为“停用”,那么它的单价就不能被修改 源代码网推荐 源代码网推荐 · 一个雇员的居住地必须与他(她)的主管的居住地相同 源代码网推荐 源代码网推荐 · 如果某个产品是某供应商唯一提供的产品,那么这个产品就不能被标记为“停用” 源代码网推荐 源代码网推荐 BLL类应该保证始终都验证应用程序的业务规则。这些验证可以直接的添加到应用他们的方法中。 源代码网推荐 源代码网推荐 想象一下,我们的业务规则表明了如果一个产品是给定的供应商的唯一产品,那么它就不能被标记为“停用”。也就是说,如果产品X是我们从供应商Y处购买的唯一一个产品,那么我们就不能将X标记为停用;然而,如果供应商Y提供给我们的一共有3样产品,分别是A、B和C,那么我们可以将其中任何一个或者三个全部都标记为“停用”。挺奇怪的业务规则,是吧?但是商业上的规则通常就是跟我们平常的感觉不太一样。 源代码网推荐 源代码网推荐 要在UpdateProducts方法中应用这个业务规则,那么我们就应该先检查Discontinued是否被设置为true。假如是这样的话,那么我们应该先调用GetProductsBySupplierID来看看我们从这个供应商处一共购买了多少产品。如果我们仅仅从这个供应商处购买了这一个产品,那么我们就抛出一个ApplicationException。 源代码网推荐 源代码网推荐 UpdateProduct 源代码网推荐 源代码网推荐 1public bool UpdateProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit, 源代码网推荐 2 decimal unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel, 源代码网推荐 3 bool discontinued, int productID) 源代码网推荐 4{ 源代码网推荐 5 Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID); 源代码网推荐 6 if (products.Count == 0) 源代码网推荐 7 // 没有找到匹配项,返回false 源代码网推荐 8 return false; 源代码网推荐 9 源代码网推荐 10 Northwind.ProductsRow product = products[0]; 源代码网推荐 11 源代码网推荐 12 // 业务规则检查 – 不能停用某供应商所提供的唯一一个产品 源代码网推荐 13 if (discontinued) 源代码网推荐 14 { 源代码网推荐 15 // 获取我们从这个供应商处获得的所有产品 源代码网推荐 16 Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID); 源代码网推荐 17 源代码网推荐 18 if (productsBySupplier.Count == 1) 源代码网推荐 19 // 这是我们从这个供应商处获得的唯一一个产品 源代码网推荐 20 throw new ApplicationException("You cannot mark a product as discontinued if its the only product purchased from a supplier"); 源代码网推荐 21 } 源代码网推荐 22 源代码网推荐 23 product.ProductName = productName; 源代码网推荐 24 if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value; 源代码网推荐 25 if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value; 源代码网推荐 26 if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit; 源代码网推荐 27 if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; 源代码网推荐 28 if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value; 源代码网推荐 29 if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value; 源代码网推荐 30 if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value; 源代码网推荐 31 product.Discontinued = discontinued; 源代码网推荐 32 源代码网推荐 33 // 更新产品记录 源代码网推荐 34 int rowsAffected = Adapter.Update(product); 源代码网推荐 35 源代码网推荐 36 // 如果刚好更新了一条记录,则返回true,否则返回false 源代码网推荐 37 return rowsAffected == 1; 源代码网推荐 38} 源代码网推荐 39 源代码网推荐 源代码网推荐 在表示层中响应验证错误 源代码网推荐 源代码网推荐 当我们从表示层中调用BLL时,我们可以决定是否要处理某个可能会被抛出的异常或者让它直接抛给ASP.NET(这样将会引发HttpApplication的出错事件)。在使用BLL的时候,如果要以编程的方式处理一个异常,我们可以使用try...catch块,就像下面的示例一样: 源代码网推荐 源代码网推荐 1 ProductsBLL productLogic = new ProductsBLL(); 源代码网推荐 2 源代码网推荐 3 // 更新ProductID为1的产品信息 源代码网推荐 4 try 源代码网推荐 5 { 源代码网推荐 6 // 这个操作将会失败,因为我们试图使用一个小于0的UnitPrice 源代码网推荐 7 productLogic.UpdateProduct("Scott"s Tea", 1, 1, null, -14m, 10, null, null, false, 1); 源代码网推荐 8 } 源代码网推荐 9 catch (ArgumentException ae) 源代码网推荐 10 { 源代码网推荐 11 Response.Write("There was a problem: " + ae.Message); 源代码网推荐 12 } 源代码网推荐 源代码网推荐 我们将在后面的教程中看到,当通过一个数据Web控件(data Web Control)来进行插入、修改或删除操作数据时,处理从BLL中抛出的异常可以直接在一个Event Handler中进行,而不需要使用try…catch块来包装代码。 源代码网推荐 源代码网推荐 总结 源代码网推荐 源代码网推荐 一个具有良好架构的应用程序都拥有清晰的层次结构,每一个层次都封装了一个特定的角色。在本教程的第一篇中,我们用类型化数据集创建了一个数据访问层;这一篇中,我们又建立了一个业务逻辑层,它由App_Code中一系列的类构成,并调用DAL中相应的方法。BLL为我们的应用程序实现了字段级和业务级的逻辑。除了创建一个独立的BLL,就像我们在本节中所做的那样,另外一个选择是使用partial类来扩展TableAdapter中的方法。然而,使用这个技术并不能使我们可以重写已经存在的方法,也不能将我们的DAL和BLL分开得足够清晰。 源代码网推荐 源代码网推荐 完成了DAL和BLL之后,我们就准备开始处理表示层了。在下一个教程中,我们将简单的介绍一些数据访问的主题,并为整个教程定义一个一致的页面呈现 源代码网推荐 做人要厚道,请注明转自酷网动力(www.ASPCOOL.COM)。 源代码网推荐 源代码网供稿. |
