WEB三层结构的设计思路
1.DAL层的设计:
1.1.强类型的DataTable并不包括如何访问对应底层的数据表的任何信息,DataTable的作用是在分层间传输数据。要获取用来填充DataTable的
数据 ,我们使用TableAdapter类,它提供了数据访问层的功能,对于TableAdapter类中的自定义方法,我们使用部分类来保存。
using System;
using System.Data;
using NorthwindTableAdapters;
public partial class Northwind
{
public partial class SuppliersRow
{
public Northwind.ProductsDataTable GetProducts()
{
ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
return productsAdapter.GetProductsBySupplierID(this.SupplierID);
}
}
}
1.2.如果使用DB直接模式,DataSet设计器会根据SELECT语句自动生成插入,更新,删除方法,而且不会受SELECT子句中的子查询的影响。但如果
使用JOIN语句的话,DataSet设计器就不能自动生成插入,更新,以及删除数据库记录的方法了,这时,必须手工设置InsertCommand,
UpdateCommand和DeleteCommand属性值.
SELECT ProductID, ProductName, SupplierID, CategoryID,
QuantityPerUnit, UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
(SELECT CategoryName FROM Categories
WHERE Categories.CategoryID = Products.CategoryID) as CategoryName,
(SELECT CompanyName FROM Suppliers
WHERE Suppliers.SupplierID = Products.SupplierID) as SupplierName
FROM Products
2.BLL层的设计:
2.1简单的调用DAL中的方法
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;
[System.ComponentModel.DataObject]
public class ProductsBLL
{
private ProductsTableAdapter _productsAdapter = null;
protected ProductsTableAdapter Adapter
{
get {
if (_productsAdapter == null)
_productsAdapter = new ProductsTableAdapter();
return _productsAdapter;
}
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, true)]
public Northwind.ProductsDataTable GetProducts()
{
return Adapter.GetProducts();
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductByProductID(int productID)
{
return Adapter.GetProductByProductID(productID);
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
{
return Adapter.GetProductsByCategoryID(categoryID);
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
{
return Adapter.GetProductsBySupplierID(supplierID);
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Insert, true)]
public bool AddProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit,
decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel,
bool discontinued)
{
// 新建一个ProductRow实例
Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
Northwind.ProductsRow product = products.NewProductsRow();
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// 添加新产品
products.AddProductsRow(product);
int rowsAffected = Adapter.Update(products);
// 如果刚好新增了一条记录,则返回true,否则返回false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit,
decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel,
bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// 没有找到匹配的记录,返回false
return false;
Northwind.ProductsRow product = products[0];
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// 更新产品记录
int rowsAffected = Adapter.Update(product);
// 如果刚好更新了一条记录,则返回true,否则返回false
return rowsAffected == 1;
}
[System.ComponentModel.DataObjectMethodAttribute(System.ComponentModel.DataObjectMethodType.Delete, true)]
public bool DeleteProduct(int productID)
{
int rowsAffected = Adapter.Delete(productID);
// 如果刚好删除了一条记录,则返回true,否则返回false
return rowsAffected == 1;
}
}
2.2.给DataRow添加字段级验证
新建一个名为ProductsDataTable.ColumnChanging.cs的类文件
public partial class Northwind
{
public partial class ProductsDataTable
{
public override void BeginInit()
{
this.ColumnChanging += ValidateColumn;
}
void ValidateColumn(object sender, DataColumnChangeEventArgs e)
{
if(e.Column.Equals(this.UnitPriceColumn))
{
if(!Convert.IsDBNull(e.ProposedValue) && (decimal)e.ProposedValue < 0)
{
throw new ArgumentException("UnitPrice cannot be less than zero", "UnitPrice");
}
}
else if (e.Column.Equals(this.UnitsInStockColumn) ||
e.Column.Equals(this.UnitsOnOrderColumn) ||
e.Column.Equals(this.ReorderLevelColumn))
{
if (!Convert.IsDBNull(e.ProposedValue) && (short)e.ProposedValue < 0)
{
throw new ArgumentException(string.Format("{0} cannot be less than zero", e.Column.ColumnName),
e.Column.ColumnName);
}
}
}
}
}
2.3添加业务规则
public bool UpdateProduct(string productName, int? supplierID, int? categoryID, string quantityPerUnit,
decimal? unitPrice, short? unitsInStock, short? unitsOnOrder, short? reorderLevel,
bool discontinued, int productID)
{
Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
if (products.Count == 0)
// 没有找到匹配项,返回false
return false;
Northwind.ProductsRow product = products[0];
// 业务规则检查 – 不能停用某供应商所提供的唯一一个产品
if (discontinued)
{
// 获取我们从这个供应商处获得的所有产品
Northwind.ProductsDataTable productsBySupplier = Adapter.GetProductsBySupplierID(product.SupplierID);
if (productsBySupplier.Count == 1)
// 这是我们从这个供应商处获得的唯一一个产品
throw new ApplicationException("You cannot mark a product as discontinued if its the only product purchased
from a supplier");
}
product.ProductName = productName;
if (supplierID == null) product.SetSupplierIDNull(); else product.SupplierID = supplierID.Value;
if (categoryID == null) product.SetCategoryIDNull(); else product.CategoryID = categoryID.Value;
if (quantityPerUnit == null) product.SetQuantityPerUnitNull(); else product.QuantityPerUnit = quantityPerUnit;
if (unitPrice == null) product.SetUnitPriceNull(); else product.UnitPrice = unitPrice.Value;
if (unitsInStock == null) product.SetUnitsInStockNull(); else product.UnitsInStock = unitsInStock.Value;
if (unitsOnOrder == null) product.SetUnitsOnOrderNull(); else product.UnitsOnOrder = unitsOnOrder.Value;
if (reorderLevel == null) product.SetReorderLevelNull(); else product.ReorderLevel = reorderLevel.Value;
product.Discontinued = discontinued;
// 更新产品记录
int rowsAffected = Adapter.Update(product);
// 如果刚好更新了一条记录,则返回true,否则返回false
return rowsAffected == 1;
}
2.4在表示层中对异常的处理
那么表示层如何捕捉到异常?一种是通过try...catch()的方法,另一种就是通过WEB控件中的事件来捕捉异常。
在插入、更新或删除操作的过程中,数据Web控件和ObjectDataSource控件都包含了pre- 和post-级的事件,它们记录着当前的操作。当使用一
个可编辑的GridView时,事件触发顺序如下:ObjectDataSource_Updating-->GridView_RowUpdating-->GridView_RowUpdated--
>ObjectDataSource_Updated
我们可以为这些发生在操作之前的事件创建事件处理程序,目的是自定义输入参数;为发生在操作之后的事件创建事件处理,目的是检测和相应操作的结果。Post-level的事件处理程序通常用作侦测在操作过程中是否出现了一个异常。当面对一个异常时,这些post-level的事件处理程序可以随意地处理该异常。
示例1:
ProductsBLL productLogic = new ProductsBLL();
// 更新ProductID为1的产品信息
try
{
// 这个操作将会失败,因为我们试图使用一个小于0的UnitPrice
productLogic.UpdateProduct("Scott's Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
Response.Write("There was a problem: " + ae.Message);
}
示例2:
protected void GridView1_RowUpdated(object sender, GridViewUpdatedEventArgs e)
{
if (e.Exception != null)
{
// Display a user-friendly message
ExceptionDetails.Visible = true;
ExceptionDetails.Text = "There was a problem updating the product. ";
if (e.Exception.InnerException != null)
{
Exception inner = e.Exception.InnerException;
if (inner is System.Data.Common.DbException)
ExceptionDetails.Text += "Our database is currently experiencing problems. Please try again later.";
else if (inner is NoNullAllowedException)
ExceptionDetails.Text += "There are one or more required fields that are missing.";
else if (inner is ArgumentException)
{
string paramName = ((ArgumentException)inner).ParamName;
ExceptionDetails.Text += string.Concat("The ", paramName, " value is illegal.");
}
else if (inner is ApplicationException)ExceptionDetails.Text += inner.Message;
}
// Indicate that the exception has been handled
e.ExceptionHandled = true;
// Keep the row in edit mode
e.KeepInEditMode = true;
}
}
总之,每个工单由3部分组成(DAL,BLL,Web),DAL和BLL都有相应有命名空间.
如工单MPR:则可以建一个NetSoft.MPR.DAL和NetSoft.MPR.BLL,然后再做一个WEB模板。这样就可以实现三层结构,而且可以使代码量较少。