![]() 圖一:BLL將表示層與DAL隔開了,并且加入了業(yè)務(wù)規(guī)則 |
![]() 圖二:在BLL文件夾中添加4個(gè)新的類 |
1using System; 2using System.Data; 3using System.Configuration; 4using System.Web; 5using System.Web.Security; 6using System.Web.UI; 7using System.Web.UI.WebControls; 8using System.Web.UI.WebControls.WebParts; 9using System.Web.UI.HtmlControls; 10using NorthwindTableAdapters; 11 12[System.ComponentModel.DataObject] 13public class ProductsBLL 14{ 15 private ProductsTableAdapter _productsAdapter = null; 16 protected ProductsTableAdapter Adapter 17 { 18 get { 19 if (_productsAdapter == null) 20 _productsAdapter = new ProductsTableAdapter(); 21 22 return _productsAdapter; 23 } 24 } 25 26 27[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)] 28 public Northwind.ProductsDataTable GetProducts() 29 { 30 return Adapter.GetProducts(); 31 } 32 33 [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)] 34 public Northwind.ProductsDataTable GetProductByProductID(int productID) 35 { 36 return Adapter.GetProductByProductID(productID); 37 } 38 39[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)] 40 public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID) 41 { 42 return Adapter.GetProductsByCategoryID(categoryID); 43 } 44 45[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)] 46 public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID) 47 { 48 return Adapter.GetProductsBySupplierID(supplierID); 49 } 50 [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, true)] 51 public bool AddProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit, 52 decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel, 53 bool discontinued) 54 { 55 // 新建一個(gè)ProductRow實(shí)例 56 Northwind.ProductsDataTable products = new Northwind.ProductsDataTable(); 57 Northwind.ProductsRow product = products.NewProductsRow(); 58 59 product.ProductName = productName; 60 if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value; 61 if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value; 62 if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit; 63 if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; 64 if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value; 65 if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value; 66 if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value; 67 product.Discontinued = discontinued; 68 69 // 添加新產(chǎn)品 70 products.AddProductsRow(product); 71 int rowsAffected = Adapter.Update(products); 72 73 // 如果剛好新增了一條記錄,則返回true,否則返回false 74 return rowsAffected == 1; 75 } 76 77 [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)] 78 public bool UpdateProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit, 79 decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel, 80 bool discontinued, int productID) 81 { 82 Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID); 83 if (products.Count == 0) 84 // 沒有找到匹配的記錄,返回false 85 return false; 86 87 Northwind.ProductsRow product = products[0]; 88 89 product.ProductName = productName; 90 if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value; 91 if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value; 92 if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit; 93 if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value; 94 if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value; 95 if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value; 96 if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value; 97 product.Discontinued = discontinued; 98 99 // 更新產(chǎn)品記錄 100 int rowsAffected = Adapter.Update(product); 101 102 // 如果剛好更新了一條記錄,則返回true,否則返回false 103 return rowsAffected == 1; 104 } 105 106 [System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Delete, true)] 107 public bool DeleteProduct(int productID) 108 { 109 int rowsAffected = Adapter.Delete(productID); 110 111 // 如果剛好刪除了一條記錄,則返回true,否則返回false 112 return rowsAffected == 1; 113 } 114} 115 |
GetProducts、GetProductByProductID、GetProductsByCategoryID以及 GetProductBySuppliersID等方法都僅僅是簡(jiǎn)簡(jiǎn)單單的直接調(diào)用DAL中的方法來返回?cái)?shù)據(jù)。不過在有的情況下,我們還可能需要給它們實(shí)現(xiàn)一些業(yè)務(wù)規(guī)則(比如說授權(quán)規(guī)則,不同的用戶或不用角色應(yīng)該可以看到不同的數(shù)據(jù)),現(xiàn)在我們簡(jiǎn)單的將它們做成這樣就可以了。那么,對(duì)于這些方法來說,BLL僅僅是作為表示層與DAL之間的代理。
AddProduct和UpdateProduct這兩個(gè)方法都使用參數(shù)中的那些產(chǎn)品信息去添加或是更新一條產(chǎn)品記錄。由于Product表中有許多字段都允許空值(CategoryID、SupplierID、UnitPrice……等等),所以AddProduct和UpdateProduct中相應(yīng)的參數(shù)就使用nullable types。Nullable types是.NET 2.0中新提供的一種用于標(biāo)明一個(gè)值類型是否可以為空的技術(shù)。在C#中,你可以在一個(gè)允許為空的值類型后面加上一個(gè)問號(hào)(比如,int x;)。關(guān)于Nullable Types的詳細(xì)信息,你可以參考C# Programming Guide。
由于插入、修改和刪除可能不會(huì)影響任何行,所以這三種方法均返回一個(gè)bool值用于表示操作是否成功。比如說,頁(yè)面開發(fā)人員使用一個(gè)并不存在的ProductID去調(diào)用DeleteProduct,很顯然,提交給數(shù)據(jù)庫(kù)的DELETE語(yǔ)句將不會(huì)有任何作用,所以DeleteProduct會(huì)返回false。
注意:當(dāng)我們?cè)谔砑踊蚋乱粋€(gè)產(chǎn)品的詳細(xì)信息時(shí),都是接受由產(chǎn)品信息組成的一個(gè)標(biāo)量列表,而不是直接接受一個(gè)ProductsRow實(shí)例。因?yàn)镻roductsRow是繼承于ADO.NET的DataRow,而DataRow沒有默認(rèn)的無(wú)參構(gòu)造函數(shù),為了創(chuàng)建一個(gè)ProductsRow的實(shí)例,我們必須先創(chuàng)建一個(gè)ProductsDataTable的實(shí)例,然后調(diào)用它的NewProductRow方法(就像我們?cè)贏ddProduct方法中所做的那樣)。不過,當(dāng)我在使用ObjectDataSource來插入或更新時(shí),這樣做的缺點(diǎn)就會(huì)暴露出來了。簡(jiǎn)單的講,ObjectDataSource會(huì)試圖為輸入的參數(shù)創(chuàng)建一個(gè)實(shí)例,如果BLL方法希望得到一個(gè)ProductsRow,那么ObjectDataSource就將會(huì)試圖去創(chuàng)建一個(gè),不過很顯然,這樣的操作一定會(huì)失敗,因?yàn)闆]有一個(gè)默認(rèn)的無(wú)參構(gòu)造函數(shù)。這個(gè)問題的詳細(xì)信息,可以在ASP.NET論壇的以下兩個(gè)帖子中找到: Updating ObjectDataSources with Strongly-Typed DataSets、Problem With ObjectDataSource and Strongly-Typed DataSet。
之后,在AddProduct和UpdateProduct中,我們創(chuàng)建了一個(gè)ProductsRow實(shí)例,并將傳入的參數(shù)賦值給它。當(dāng)給一個(gè)DataRow的DataColumns賦值時(shí),各種字段級(jí)的有效性驗(yàn)證都有可能會(huì)被觸發(fā)。因此,我們應(yīng)該手工的驗(yàn)證一下傳入的參數(shù)以保證傳遞給BLL方法的數(shù)據(jù)是有效的。不幸的是,Visual Studio生成的強(qiáng)類型數(shù)據(jù)集(strongly-typed DataRow)并沒有使用nullable values。要表明DataRow中的一個(gè)DataColumn可以接受空值,我們就必須得使用SetColumnNameNull方法。
在UpdateProduct中,我們先使用GetProductByProductID(productID)方法將需要更新的產(chǎn)品信息讀取出來。這樣做好像沒有什么必要,不過我們將在之后的關(guān)于并發(fā)優(yōu)化(Optimistic concurrency)的課程中證明這個(gè)額外的操作是有它的作用的。并發(fā)優(yōu)化是一種保證兩個(gè)用戶同時(shí)操作一個(gè)數(shù)據(jù)而不會(huì)發(fā)生沖突的技術(shù)。獲取整條記錄同時(shí)也可以使創(chuàng)建一個(gè)僅更新DataRow的一部分列的方法更加容易,我們可以在SuppliersBLL類中找到這樣的例子。
最后,注意我們?cè)赑roductsBLL類上面加上了DataObject 標(biāo)簽(就是在類聲明語(yǔ)句的上面的[System.ComponentModel.DataObject]),各方法上面還有DataObjectMethodAttribute 標(biāo)簽。DataObject標(biāo)簽把這個(gè)類標(biāo)記為可以綁定到一個(gè)ObjectDataSource控件,而DataObjectMethodAttribute則說明了這個(gè)方法的目的。我們將在后面的教程中看到,ASP.NET 2.0的ObjectDataSource使從一個(gè)類中訪問數(shù)據(jù)更加容易。為了ObjectDataSource向?qū)軌驅(qū)ΜF(xiàn)有的類進(jìn)行合適的篩選,在類列表中默認(rèn)僅顯示標(biāo)記為DataObject的類。當(dāng)然,其實(shí)ProductsBLL類就算沒有這個(gè)標(biāo)簽也可以工作,但是加上它可以使我們?cè)贠bjectDataSource向?qū)е械牟僮鞲虞p松和心情愉快。
添加其他的類
完成了ProductsBLL類之后,我們還要添加一些為categories、suppliers和employees服務(wù)的類。讓我們花點(diǎn)時(shí)間來創(chuàng)建下面的類,根據(jù)上面的例子來做就是了:
· 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方法是一個(gè)值得注意的東西。這個(gè)方法提供了一個(gè)僅僅更新供應(yīng)商地址信息的接口。它首先根據(jù)指定的SupplierID讀出一個(gè)SupplierDataRow(使用GetSupplierBySupplierID方法),設(shè)置其關(guān)于地址的所有屬性,然后調(diào)用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 // 沒有找到匹配的項(xiàng),返回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 // 更新供應(yīng)商的關(guān)于地址的信息 17 int rowsAffected = Adapter.Update(supplier); 18 19 // 如果剛好更新了一條記錄,則返回true,否則返回false 20 return rowsAffected == 1; 21 } 22} 23 |
1 ProductsTableAdapter productsAdapter = new ProductsTableAdapter(); 2 GridView1.DataSource = productsAdapter.GetProducts(); 3 GridView1.DataBind(); |
1 ProductsBLL productLogic = new ProductsBLL(); 2 GridView1.DataSource = productLogic.GetProducts(); 3 GridView1.DataBind(); |
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} |
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 // 沒有找到匹配項(xiàng),返回false 8 return false; 9 10 Northwind.ProductsRow product = products[0]; 11 12 // 業(yè)務(wù)規(guī)則檢查 – 不能停用某供應(yīng)商所提供的唯一一個(gè)產(chǎn)品 13 if (discontinued) 14 { 15 // 獲取我們從這個(gè)供應(yīng)商處獲得的所有產(chǎn)品 16 Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID); 17 18 if (productsBySupplier.Count == 1) 19 // 這是我們從這個(gè)供應(yīng)商處獲得的唯一一個(gè)產(chǎn)品 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 // 更新產(chǎn)品記錄 34 int rowsAffected = Adapter.Update(product); 35 36 // 如果剛好更新了一條記錄,則返回true,否則返回false 37 return rowsAffected == 1; 38} 39 |
1 ProductsBLL productLogic = new ProductsBLL(); 2 3 // 更新ProductID為1的產(chǎn)品信息 4 try 5 { 6 // 這個(gè)操作將會(huì)失敗,因?yàn)槲覀冊(cè)噲D使用一個(gè)小于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 } |
聯(lián)系客服