• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            道。道。道

            安全特性不等于安全的特性

               :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理

            常用鏈接

            搜索

            •  

            最新評(píng)論

              導(dǎo)言

              本教程的第一節(jié)所描述的數(shù)據(jù)訪問(wèn)層(Data Access Layer,以下簡(jiǎn)稱為DAL)已經(jīng)清晰地將表示邏輯與數(shù)據(jù)訪問(wèn)邏輯區(qū)分開(kāi)了。不過(guò),即使DAL將數(shù)據(jù)訪問(wèn)的細(xì)節(jié)從表示層中分離出來(lái)了,可它卻不能處理任何的業(yè)務(wù)規(guī)則。比如說(shuō),我們可能不希望產(chǎn)品表中那些被標(biāo)記為“停用”的產(chǎn)品的“分類編號(hào)”或“供應(yīng)商編號(hào)”被更新;我們還可能需要應(yīng)用一些資歷規(guī)則,比如說(shuō)我們都不希望被比自己的資歷還要淺的人管理。另外一個(gè)比較常見(jiàn)的情況就是授權(quán),比如說(shuō)只有那些具有特殊權(quán)限的用戶可以刪除產(chǎn)品或是更改單價(jià)。

              我們其實(shí)可以將業(yè)務(wù)邏輯層(Business Logic Layer,以下簡(jiǎn)稱BLL)看作是在數(shù)據(jù)訪問(wèn)層和表示層之間進(jìn)行數(shù)據(jù)交換的橋梁,在這個(gè)章節(jié)中,我們將討論一下如何將這些業(yè)務(wù)規(guī)則集成到一個(gè)BLL中。需要說(shuō)明的是,在一個(gè)實(shí)際的應(yīng)用程序中,BLL都是以類庫(kù)(Class Library)的形式來(lái)實(shí)現(xiàn)的,不過(guò)為了簡(jiǎn)化工程的結(jié)構(gòu),在本教程中我們將BLL實(shí)現(xiàn)為App_Code文件夾中的一系列的類。圖一向我們展示了表示層、BLL以及DAL三者之間的結(jié)構(gòu)關(guān)系。


            圖一:BLL將表示層與DAL隔開(kāi)了,并且加入了業(yè)務(wù)規(guī)則

              第一步:創(chuàng)建BLL類

              我們的BLL由4個(gè)類組成,每一個(gè)BLL類都對(duì)應(yīng)DAL中的一個(gè)TableAdapter,它們都從各自的TableAdapter中得到讀取、插入、修改以及刪除等方法以應(yīng)用合適的業(yè)務(wù)規(guī)則。

              為了更加清晰的區(qū)分DAL和BLL的類,我們?cè)贏pp_Code文件夾中建立兩個(gè)子文件夾,分別命名為DAL和BLL。你僅僅需要在解決方案瀏覽器(Solution Explorer)中右鍵點(diǎn)擊App_Code文件夾,并選擇新建文件夾(New Folder),就可以創(chuàng)建新的子文件夾了。建好了這兩個(gè)文件夾之后,把第一節(jié)中所創(chuàng)建的類型化數(shù)據(jù)集(Typed DataSet)移到DAL文件夾中。

              然后,在BLL文件夾中創(chuàng)建4個(gè)類文件。同樣,你僅僅需要在解決方案瀏覽器(Solution Explorer)中右鍵點(diǎn)擊BLL文件夾,并選擇新建項(xiàng)目(New Item),然后在彈出的對(duì)話框中選擇類模板(Class template)就可以創(chuàng)建新的類文件了。將這四個(gè)文件分別命名為ProductsBLL、CategoriesBLL、SuppliersBLL以及EmployeesBLL。


            圖二:在BLL文件夾中添加4個(gè)新的類

              接下來(lái),讓我們來(lái)給這些新建的類加上一些方法,簡(jiǎn)單的將第一節(jié)中的TableAdapter中的那些方法包裝起來(lái)就行了。現(xiàn)在,這些方法將只能直接使用DAL中的那些方法,我們等會(huì)再來(lái)給他們加上一些業(yè)務(wù)邏輯。

              注意:如果你使用的是Visual Studio 標(biāo)準(zhǔn)版或以上版本(也就是說(shuō),你不是用的Visual Web Developer),那么你還可以使用Class Designer來(lái)可視化的設(shè)計(jì)你的類。你可以在Class Designer Blog上得到關(guān)于Visual Studio的這項(xiàng)新功能的詳細(xì)信息。

              在ProductsBLL類中,我們一共需要為其添加7個(gè)方法:

              ·GetProducts() – 返回所有的產(chǎn)品

              ·GetProductByProductID(productID) – 返回指定ProductID的產(chǎn)品

              ·GetProductsByCategoryID(categoryID) –返回指定分類的產(chǎn)品

              ·GetProductsBySupplier(supplierID) –返回指定供應(yīng)商的產(chǎn)品

              ·AddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued) – 向數(shù)據(jù)庫(kù)中添加一條產(chǎn)品信息,并返回新添加的產(chǎn)品的ProductID

              ·UpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID) – 更新一個(gè)數(shù)據(jù)庫(kù)中已經(jīng)存在的產(chǎn)品,如果剛好更新了一條記錄,則返回true,否則返回false

              ·DeleteProduct(productID) – 刪除指定ProductID的產(chǎn)品

              ProductsBLL.cs

            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 // 沒(méi)有找到匹配的記錄,返回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中的方法來(lái)返回?cái)?shù)據(jù)。不過(guò)在有的情況下,我們還可能需要給它們實(shí)現(xiàn)一些業(yè)務(wù)規(guī)則(比如說(shuō)授權(quán)規(guī)則,不同的用戶或不用角色應(yīng)該可以看到不同的數(shù)據(jù)),現(xiàn)在我們簡(jiǎn)單的將它們做成這樣就可以了。那么,對(duì)于這些方法來(lái)說(shuō),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è)問(wèn)號(hào)(比如,int x;)。關(guān)于Nullable Types的詳細(xì)信息,你可以參考C# Programming Guide。

              由于插入、修改和刪除可能不會(huì)影響任何行,所以這三種方法均返回一個(gè)bool值用于表示操作是否成功。比如說(shuō),頁(yè)面開(kāi)發(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沒(méi)有默認(rèn)的無(wú)參構(gòu)造函數(shù),為了創(chuàng)建一個(gè)ProductsRow的實(shí)例,我們必須先創(chuàng)建一個(gè)ProductsDataTable的實(shí)例,然后調(diào)用它的NewProductRow方法(就像我們?cè)贏ddProduct方法中所做的那樣)。不過(guò),當(dāng)我在使用ObjectDataSource來(lái)插入或更新時(shí),這樣做的缺點(diǎn)就會(huì)暴露出來(lái)了。簡(jiǎn)單的講,ObjectDataSource會(huì)試圖為輸入的參數(shù)創(chuàng)建一個(gè)實(shí)例,如果BLL方法希望得到一個(gè)ProductsRow,那么ObjectDataSource就將會(huì)試圖去創(chuàng)建一個(gè),不過(guò)很顯然,這樣的操作一定會(huì)失敗,因?yàn)闆](méi)有一個(gè)默認(rèn)的無(wú)參構(gòu)造函數(shù)。這個(gè)問(wèn)題的詳細(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)并沒(méi)有使用nullable values。要表明DataRow中的一個(gè)DataColumn可以接受空值,我們就必須得使用SetColumnNameNull方法。

              在UpdateProduct中,我們先使用GetProductByProductID(productID)方法將需要更新的產(chǎn)品信息讀取出來(lái)。這樣做好像沒(méi)有什么必要,不過(guò)我們將在之后的關(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則說(shuō)明了這個(gè)方法的目的。我們將在后面的教程中看到,ASP.NET 2.0的ObjectDataSource使從一個(gè)類中訪問(wèn)數(shù)據(jù)更加容易。為了ObjectDataSource向?qū)軌驅(qū)ΜF(xiàn)有的類進(jìn)行合適的篩選,在類列表中默認(rèn)僅顯示標(biāo)記為DataObject的類。當(dāng)然,其實(shí)ProductsBLL類就算沒(méi)有這個(gè)標(biāo)簽也可以工作,但是加上它可以使我們?cè)贠bjectDataSource向?qū)е械牟僮鞲虞p松和心情愉快。
              添加其他的類

              完成了ProductsBLL類之后,我們還要添加一些為categories、suppliers和employees服務(wù)的類。讓我們花點(diǎn)時(shí)間來(lái)創(chuàng)建下面的類,根據(jù)上面的例子來(lái)做就是了:

              · 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 // 沒(méi)有找到匹配的項(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

              可以從頁(yè)面頂部的鏈接處下載BLL類的完整代碼。

              第二步:通過(guò)BLL類訪問(wèn)類型化數(shù)據(jù)集

              在本教程的第一節(jié)中,我們給出了直接使用類型化數(shù)據(jù)集的例子,不過(guò)在我們添加了BLL類之后,表示層就可以通過(guò)BLL來(lái)工作了。在本教程的第一節(jié)中的AllProducts.aspx的例子中,ProductsTableAdapter用于將產(chǎn)品列表綁定到GridView上,代碼如下所示:

            1 ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
            2 GridView1.DataSource = productsAdapter.GetProducts();
            3 GridView1.DataBind();

              要使用新的BLL類,我們所需要做的僅僅是簡(jiǎn)單的修改一下第一行代碼。用ProductBLL對(duì)象來(lái)代替 ProductsTableAdapter即可:

            1 ProductsBLL productLogic = new ProductsBLL();
            2 GridView1.DataSource = productLogic.GetProducts();
            3 GridView1.DataBind();

              BLL類也可以通過(guò)使用ObjectDataSource來(lái)清晰明了的訪問(wèn)(就像類型化數(shù)據(jù)集一樣)。我們將在接下來(lái)的教程中詳細(xì)的討論ObjectDataSource。


            圖三:GridView中顯示的產(chǎn)品列表

              第三步:給DataRow添加字段級(jí)驗(yàn)證

              字段級(jí)驗(yàn)證是指在插入或更新時(shí)檢查業(yè)務(wù)對(duì)象所涉及到的所有屬性值。拿產(chǎn)品來(lái)舉個(gè)例,某些字段級(jí)的驗(yàn)證規(guī)則如下所示:

              · ProductName字段不得超過(guò)40個(gè)字符

              · QuantityPerUnit字段不得超過(guò)20個(gè)字符

              · ProductID、ProductName以及Discontinued字段是必填的,而其他字段則是可填可不填的

              · UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段不得小于0

              這些規(guī)則可以或者說(shuō)是應(yīng)該在數(shù)據(jù)庫(kù)層被描述出來(lái)。ProductName和QuantityPerUnit字段上的字符數(shù)限制可以通過(guò)Products表中相應(yīng)列的數(shù)據(jù)類型來(lái)實(shí)現(xiàn)(分別為nvarchar(40) and nvarchar(20))。字段“是否必填”可以通過(guò)將數(shù)據(jù)庫(kù)中表的相應(yīng)列設(shè)置為“允許為NULL”來(lái)實(shí)現(xiàn)。為了保證UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段的值不小于0,可以分別在它們的相應(yīng)列上加一個(gè)約束。

              除了在數(shù)據(jù)庫(kù)中應(yīng)用了這些規(guī)則之外,它們同時(shí)也將被其應(yīng)用在DataSet上。事實(shí)上,字段長(zhǎng)度和是否允許為空等信息已經(jīng)被應(yīng)用到了各DataTable的DataColumn集合中。我們可以在數(shù)據(jù)集設(shè)計(jì)器(DataSet Designer)中看到已經(jīng)存在的字段級(jí)驗(yàn)證,從某個(gè)DataTable中選擇一個(gè)字段,然后在屬性窗口中就可以找到了。如圖四所示,ProductDataTable中的QuantityPerUnit字段允許空值并且最大長(zhǎng)度為20各字符。如果我們?cè)噲D給某個(gè)ProductsDataRow的QuantityPerUnit屬性設(shè)置一個(gè)長(zhǎng)度大于20個(gè)字符的字符串,將會(huì)有一個(gè)ArgumentException被拋出。


            圖四:DataColumn提供了基本的字段級(jí)驗(yàn)證

              不幸的是,我們不能通過(guò)屬性窗口指定一個(gè)邊界檢查,比如UnitPrice的值不能小于0。為了提供這樣的字段級(jí)驗(yàn)證,我們需要為DataTable的ColumnChanging事件建立一個(gè)Event Handler。正如上一節(jié)教程中所提到的那樣,由類型化數(shù)據(jù)集創(chuàng)建的DataSet、DataTable還有DataRow對(duì)象可以通過(guò)partial類來(lái)進(jìn)行擴(kuò)展。使用這個(gè)技術(shù),我們可以為ProductDataTable創(chuàng)建一個(gè)ColumnChanging的Event Handler。我們先在App_Code文件夾中新建一個(gè)名為ProductsDataTable.ColumnChanging.cs的類文件,如下圖所示。


            圖五:在App_Code文件夾中添加新類

              然后,給ColumnChanging事件創(chuàng)建一個(gè)Event handler,以保證UnitPrice、UnitsInStock、UnitsOnOrder以及ReorderLevel字段的值不小于0。如果這些列的值超出范圍就拋出一個(gè)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類添加業(yè)務(wù)規(guī)則

              除了字段級(jí)的驗(yàn)證,可能還有一些不能在單個(gè)列中表示的包含不同實(shí)體或概念的更高級(jí)的業(yè)務(wù)規(guī)則,比如:

              · 如果一個(gè)產(chǎn)品被標(biāo)記為“停用”,那么它的單價(jià)就不能被修改

              · 一個(gè)雇員的居住地必須與他(她)的主管的居住地相同

              · 如果某個(gè)產(chǎn)品是某供應(yīng)商唯一提供的產(chǎn)品,那么這個(gè)產(chǎn)品就不能被標(biāo)記為“停用”

              BLL類應(yīng)該保證始終都驗(yàn)證應(yīng)用程序的業(yè)務(wù)規(guī)則。這些驗(yàn)證可以直接的添加到應(yīng)用他們的方法中。

              想象一下,我們的業(yè)務(wù)規(guī)則表明了如果一個(gè)產(chǎn)品是給定的供應(yīng)商的唯一產(chǎn)品,那么它就不能被標(biāo)記為“停用”。也就是說(shuō),如果產(chǎn)品X是我們從供應(yīng)商Y處購(gòu)買的唯一一個(gè)產(chǎn)品,那么我們就不能將X標(biāo)記為停用;然而,如果供應(yīng)商Y提供給我們的一共有3樣產(chǎn)品,分別是A、B和C,那么我們可以將其中任何一個(gè)或者三個(gè)全部都標(biāo)記為“停用”。挺奇怪的業(yè)務(wù)規(guī)則,是吧?但是商業(yè)上的規(guī)則通常就是跟我們平常的感覺(jué)不太一樣。

              要在UpdateProducts方法中應(yīng)用這個(gè)業(yè)務(wù)規(guī)則,那么我們就應(yīng)該先檢查Discontinued是否被設(shè)置為true。假如是這樣的話,那么我們應(yīng)該先調(diào)用GetProductsBySupplierID來(lái)看看我們從這個(gè)供應(yīng)商處一共購(gòu)買了多少產(chǎn)品。如果我們僅僅從這個(gè)供應(yīng)商處購(gòu)買了這一個(gè)產(chǎn)品,那么我們就拋出一個(gè)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 // 沒(méi)有找到匹配項(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

              在表示層中響應(yīng)驗(yàn)證錯(cuò)誤

              當(dāng)我們從表示層中調(diào)用BLL時(shí),我們可以決定是否要處理某個(gè)可能會(huì)被拋出的異常或者讓它直接拋給ASP.NET(這樣將會(huì)引發(fā)HttpApplication的出錯(cuò)事件)。在使用BLL的時(shí)候,如果要以編程的方式處理一個(gè)異常,我們可以使用try...catch塊,就像下面的示例一樣:

            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 }

              我們將在后面的教程中看到,當(dāng)通過(guò)一個(gè)數(shù)據(jù)Web控件(data Web Control)來(lái)進(jìn)行插入、修改或刪除操作數(shù)據(jù)時(shí),處理從BLL中拋出的異常可以直接在一個(gè)Event Handler中進(jìn)行,而不需要使用try…catch塊來(lái)包裝代碼。

              總結(jié)

              一個(gè)具有良好架構(gòu)的應(yīng)用程序都擁有清晰的層次結(jié)構(gòu),每一個(gè)層次都封裝了一個(gè)特定的角色。在本教程的第一篇中,我們用類型化數(shù)據(jù)集創(chuàng)建了一個(gè)數(shù)據(jù)訪問(wèn)層;這一篇中,我們又建立了一個(gè)業(yè)務(wù)邏輯層,它由App_Code中一系列的類構(gòu)成,并調(diào)用DAL中相應(yīng)的方法。BLL為我們的應(yīng)用程序?qū)崿F(xiàn)了字段級(jí)和業(yè)務(wù)級(jí)的邏輯。除了創(chuàng)建一個(gè)獨(dú)立的BLL,就像我們?cè)诒竟?jié)中所做的那樣,另外一個(gè)選擇是使用partial類來(lái)擴(kuò)展TableAdapter中的方法。然而,使用這個(gè)技術(shù)并不能使我們可以重寫已經(jīng)存在的方法,也不能將我們的DAL和BLL分開(kāi)得足夠清晰。

              完成了DAL和BLL之后,我們就準(zhǔn)備開(kāi)始處理表示層了。在下一個(gè)教程中,我們將簡(jiǎn)單的介紹一些數(shù)據(jù)訪問(wèn)的主題,并為整個(gè)教程定義一個(gè)一致的頁(yè)面呈現(xiàn)。
            posted on 2007-11-15 21:00 獨(dú)孤九劍 閱讀(172) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Learn articles
            伊人久久大香线蕉影院95| 三级三级久久三级久久| 人妻无码中文久久久久专区| 亚洲伊人久久综合影院| 亚洲?V乱码久久精品蜜桃| 无码国内精品久久人妻麻豆按摩 | 77777亚洲午夜久久多人| 亚洲国产天堂久久综合| 久久久久久久波多野结衣高潮 | 久久精品国产欧美日韩| 久久国产精品无码网站| 亚洲美日韩Av中文字幕无码久久久妻妇| 久久久艹| 久久亚洲精品人成综合网| 久久被窝电影亚洲爽爽爽| 久久91精品综合国产首页| 亚洲а∨天堂久久精品9966| 伊人久久大香线蕉亚洲五月天| 久久99精品久久久久久hb无码| 久久免费视频观看| 亚洲精品99久久久久中文字幕| 狠狠综合久久AV一区二区三区| 国产高潮国产高潮久久久| 99久久精品国产一区二区| 日韩十八禁一区二区久久| 狠狠色婷婷久久综合频道日韩| 国产精品欧美久久久天天影视| 久久人人超碰精品CAOPOREN| 77777亚洲午夜久久多人| 日韩欧美亚洲综合久久影院d3| 午夜精品久久久久久影视riav| 久久亚洲春色中文字幕久久久| 久久精品亚洲精品国产欧美| 午夜精品久久久久久毛片| 99久久无码一区人妻| 亚洲色欲久久久综合网东京热| 色综合久久久久网| 久久精品蜜芽亚洲国产AV| 三级韩国一区久久二区综合| 久久久青草久久久青草| 日韩精品久久无码中文字幕|