创建业务逻辑层 (VB)
本教程介绍如何将业务规则集中到业务逻辑层 (BLL) 中,该层充当表示层和 DAL 之间数据交换的中介。
简介
在第一个教程中创建的数据访问层 (DAL) 将数据访问逻辑与呈现逻辑完全分离。 但是,虽然 DAL 将数据访问详细信息与表示层完全分离,但它不强制实施任何可能适用的业务规则。 例如,对于我们的应用程序,我们可能希望禁止CategoryID
在字段设置为 1 时Discontinued
修改表的 Products
或 SupplierID
字段,或者我们可能希望强制实施资历规则,从而禁止员工由其后雇用的人员管理的情况。 另一种常见方案是授权,也许只有具有特定角色的用户才能删除产品或更改 UnitPrice
值。
本教程介绍如何将这些业务规则集中到业务逻辑层 (BLL) 中,该层充当表示层和 DAL 之间的数据交换的中介。 在实际应用程序中,BLL 应作为单独的类库项目实现;但是,对于这些教程,我们将实现 BLL 作为文件夹中的一系列类 App_Code
,以简化项目结构。 图 1 说明了表示层、BLL 和 DAL 之间的体系结构关系。
图 1:BLL 将表示层与数据访问层分开,并强制实施业务规则
我们不是创建单独的类来实现 我们的业务逻辑,而是直接将此逻辑放在具有分部类的类型化数据集中。 有关创建和扩展类型化数据集的示例,请参阅第一个教程。
步骤 1:创建 BLL 类
我们的 BLL 将由四个类组成,DAL 中的每个 TableAdapter 各对应一个类:其中每个 BLL 类都有用于从 DAL 中相应的 TableAdapter 中检索、插入、更新和删除的方法,并应用相应的业务规则。
为了更清晰地分隔与 DAL 和 BLL 相关的类,让我们在 App_Code
文件夹中创建两个子文件夹, DAL
即 BLL
。 只需右键单击App_Code
解决方案资源管理器中的文件夹,然后选择“新建文件夹”。 创建这两个文件夹后,将第一个教程中创建的类型化数据集移动到 DAL
子文件夹中。
接下来,在子文件夹中创建四个 BLL
BLL 类文件。 为此,请 BLL
右键单击子文件夹,选择“添加新项”,然后选择“类”模板。 将四个类命名为 ProductsBLL
、 CategoriesBLL
、 SuppliersBLL
和 EmployeesBLL
。
图 2:向 文件夹添加四个新类App_Code
接下来,让我们向每个类添加方法,以包装第一个教程中为 TableAdapters 定义的方法。 目前,这些方法将直接调用 DAL;稍后将返回以添加任何所需的业务逻辑。
注意
如果使用 Visual Studio Standard Edition 或更高版本 (即未使用 Visual Web Developer) ,则可以选择使用类Designer直观地设计类。 有关 Visual Studio 中此新功能的详细信息,请参阅类Designer博客。
对于 类, 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
<System.ComponentModel.DataObject()> _
Public Class ProductsBLL
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
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, True)> _
Public Function GetProducts() As Northwind.ProductsDataTable
Return Adapter.GetProducts()
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductByProductID(ByVal productID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductByProductID(productID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsByCategoryID(ByVal categoryID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsByCategoryID(categoryID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsBySupplierID(ByVal supplierID As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsBySupplierID(supplierID)
End Function
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Insert, True)> _
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()
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
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
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)
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()
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
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Delete, True)> _
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
列可以接受 NULL
(CategoryID
、 SupplierID
和 UnitPrice
的值(例如几个) ),因此,对于 AddProduct
和 UpdateProduct
映射到此类列的输入参数使用 可为 null 的类型。 可以为 Null 的类型是 .NET 2.0 的新增功能,它提供一种技术来指示值类型是否应改为 Nothing
。 有关详细信息,请参阅 Paul Vick 的博客文章 “关于 Nullable 类型和 VB 的真相” 和 “可为 Null” 结构的技术文档。
这三种方法都返回一个布尔值,指示是插入、更新或删除了行,因为该操作不会导致受影响的行。 例如,如果页面开发人员为不存在的产品调用DeleteProduct
传入 的 ,DELETE
则颁发给数据库的语句将没有任何影响,因此该方法DeleteProduct
将返回 False
ProductID
。
请注意,在添加新产品或更新现有产品时,我们将新产品或修改后的产品的字段值作为标量列表,而不是接受 ProductsRow
实例。 之所以选择此方法, ProductsRow
是因为 类派生自 ADO.NET DataRow
类,该类没有默认的无参数构造函数。 为了创建新ProductsRow
实例,必须首先创建一个ProductsDataTable
实例,然后调用其NewProductRow()
方法 (我们在) 中执行。AddProduct
当我们转向使用 ObjectDataSource 插入和更新产品时,这一缺点会回过头来。 简言之,ObjectDataSource 将尝试创建输入参数的实例。 如果 BLL 方法需要实例 ProductsRow
,则 ObjectDataSource 将尝试创建一个实例,但由于缺少默认无参数构造函数而失败。 有关此问题的详细信息,请参阅以下两篇 ASP.NET 论坛文章: 使用 Strongly-Typed 数据集更新 ObjectDataSources 和 ObjectDataSource 和 Strongly-Typed DataSet 的问题。
接下来,在 和 UpdateProduct
中AddProduct
,代码将创建一个ProductsRow
实例,并使用刚刚传入的值填充该实例。 向 DataRow 的 DataColumns 分配值时,可能会进行各种字段级验证检查。 因此,手动将传入的值放回 DataRow 有助于确保将数据传递到 BLL 方法的有效性。 遗憾的是,Visual Studio 生成的强类型 DataRow 类不使用可为空的类型。 相反,为了指示 DataRow 中的特定 DataColumn 应对应于数据库值,NULL
SetColumnNameNull()
必须使用 方法。
在 中 UpdateProduct
,我们首先在产品中加载,以使用 GetProductByProductID(productID)
进行更新。 虽然这似乎是不必要的数据库行程,但在探索乐观并发的未来教程中,此额外行程将被证明是有价值的。 乐观并发是一种技术,可确保同时处理相同数据的两个用户不会意外地覆盖彼此的更改。 抓取整个记录还可以更轻松地在 BLL 中创建仅修改 DataRow 列子集的更新方法。 浏览 类时, SuppliersBLL
我们将看到这样的示例。
最后,请注意 ProductsBLL
,类应用了 DataObject 属性 , [System.ComponentModel.DataObject]
(语法紧邻文件顶部的类语句) 并且方法具有 DataObjectMethodAttribute 属性。 特性 DataObject
将类标记为适合绑定到 ObjectDataSource 控件的对象,而 DataObjectMethodAttribute
指示方法的用途。 正如我们在将来的教程中看到的那样,ASP.NET 2.0 的 ObjectDataSource 可以轻松地以声明方式访问类中的数据。 为了帮助筛选在 ObjectDataSource 的向导中要绑定到的可能类的列表,默认情况下,向导的下拉列表中仅显示标记为 DataObjects
的类。 类 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)
值得注意的一种方法是 SuppliersBLL
类的 UpdateSupplierAddress
方法。 此方法提供一个接口,用于仅更新供应商的地址信息。 在内部,此方法使用 GetSupplierBySupplierID
) 在 对象中SupplierDataRow
读取指定 supplierID
(,设置其与地址相关的属性,然后向下SupplierDataTable
调用 的 Update
方法。 方法 UpdateSupplierAddress
如下:
<System.ComponentModel.DataObjectMethodAttribute _
(System.ComponentModel.DataObjectMethodType.Update, True)> _
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
End If
End Function
有关 BLL 类的完整实现,请参阅本文的下载。
步骤 2:通过 BLL 类访问类型化数据集
在第一个教程中,我们看到了以编程方式直接使用类型化数据集的示例,但添加 BLL 类后,表示层应改为针对 BLL 工作。 在第 AllProducts.aspx
一个教程的示例中, ProductsTableAdapter
使用 将产品列表绑定到 GridView,如以下代码所示:
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
若要使用新的 BLL 类,只需将第一行代码替换为 ProductsTableAdapter
对象 ProductBLL
即可:
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
还可以以声明方式访问 BLL 类, (类型化数据集也可以使用 ObjectDataSource) 。 我们将在以下教程中更详细地讨论 ObjectDataSource。
图 3:产品列表显示在 GridView 中 (单击以查看全尺寸图像)
步骤 3:向 DataRow 类添加 Field-Level 验证
字段级验证是插入或更新时与业务对象的属性值相关的检查。 产品的一些字段级验证规则包括:
- 字段
ProductName
的长度必须为 40 个字符或更少 - 字段
QuantityPerUnit
的长度必须为 20 个字符或更少 ProductID
、ProductName
和Discontinued
字段是必需的,但所有其他字段都是可选的UnitPrice
、UnitsInStock
、UnitsOnOrder
和ReorderLevel
字段必须大于或等于零
这些规则可以而且应该在数据库级别表示。 和 字段的ProductName
字符限制分别由表中这些列Products
的数据类型捕获 (nvarchar(40)
和 nvarchar(20)
) 。QuantityPerUnit
如果数据库表列允许 NULL
,则表示字段是否为必填字段和可选字段。 存在四个检查约束,确保只有大于或等于零的值才能将其转换为 UnitPrice
、UnitsInStock
、 UnitsOnOrder
或 ReorderLevel
列。
除了在数据库中强制实施这些规则外,还应在 DataSet 级别强制实施这些规则。 事实上,已为每个 DataTable 的 DataColumns 集捕获字段长度以及值是必需值还是可选值。 若要查看自动提供的现有字段级验证,请转到 DataSet Designer,从其中一个 DataTable 中选择一个字段,然后转到属性窗口。 如图 4 所示, QuantityPerUnit
中的 ProductsDataTable
DataColumn 的最大长度为 20 个字符,并且允许 NULL
值。 如果尝试将 的 QuantityPerUnit
属性设置为ProductsDataRow
长度超过 20 个字符的字符串值,ArgumentException
则会引发 。
图 4:DataColumn 提供基本 Field-Level 验证 (单击以查看全尺寸图像)
遗憾的是,无法通过属性窗口指定边界检查,例如UnitPrice
值必须大于或等于零。 为了提供这种类型的字段级验证,我们需要为 DataTable 的 ColumnChanging 事件创建事件处理程序。 如 前面的教程中所述,可以通过使用分部类扩展类型化数据集创建的 DataSet、DataTable 和 DataRow 对象。 使用此方法,我们可以为 ProductsDataTable
类创建ColumnChanging
事件处理程序。 首先在名为 ProductsDataTable.ColumnChanging.vb
的App_Code
文件夹中创建类。
图 5:向文件夹添加新类 App_Code
(单击以查看全尺寸图像)
接下来,为 ColumnChanging
事件创建一个事件处理程序,以确保 UnitPrice
(NULL
的 、UnitsInStock
、 UnitsOnOrder
和 ReorderLevel
列值) 大于或等于零。 如果任何此类列在范围外,则引发 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), _
e.Column.ColumnName)
End If
End If
End Sub
End Class
End Class
步骤 4:将自定义业务规则添加到 BLL 的类
除了字段级验证外,还可能存在涉及不同实体或概念的高级自定义业务规则,这些实体或概念无法在单列级别表达,例如:
- 如果产品已停产,
UnitPrice
则无法更新 - 员工的居住国家/地区必须与经理的居住国相同
- 如果产品是供应商提供的唯一产品,则产品不能停产
BLL 类应包含检查,以确保遵守应用程序的业务规则。 这些检查可以直接添加到它们所应用的方法中。
假设我们的业务规则规定,如果某个产品是给定供应商提供的唯一产品,则无法将其标记为停产。 也就是说,如果产品 X 是我们从供应商 Y 购买的唯一产品,则我们不能将 X 标记为已停产:但是,如果供应商 Y 向我们提供了三种产品 ,即 A、 B 和 C,那么我们可以将所有这些产品标记为已停产。 一个奇怪的业务规则,但业务规则和常识并不总是一致的!
若要在 方法中 UpdateProducts
强制实施此业务规则,首先检查是否 Discontinued
设置为 True
,如果是,我们将调用 GetProductsBySupplierID
以确定从此产品的供应商购买了多少产品。 如果只从此供应商购买一种 ApplicationException
产品,我们将引发 。
<System.ComponentModel.DataObjectMethodAttribute_
(System.ComponentModel.DataObjectMethodType.Update, True)> _
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)
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()
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
的事件 Error
) 。 若要在以编程方式使用 BLL 时处理异常,可以使用 Try...Catch 块,如以下示例所示:
Dim productLogic As New ProductsBLL()
Try
productLogic.UpdateProduct("Scotts Tea", 1, 1, Nothing, _
-14, 10, Nothing, Nothing, False, 1)
Catch ae As ArgumentException
Response.Write("There was a problem: " & ae.Message)
End Try
正如我们在将来的教程中所看到的,处理使用数据 Web 控件插入、更新或删除数据时从 BLL 冒升的异常可以直接在事件处理程序中处理,而不必将代码包装在块中 Try...Catch
。
总结
架构良好的应用程序被设计成不同的层,每个层都封装了一个特定的角色。 在本系列文章的第一个教程中,我们使用类型化数据集创建了数据访问层;在本教程中,我们在应用程序的 App_Code
文件夹中构建了一个业务逻辑层作为一系列类,这些类向下调用到 DAL 中。 BLL 为应用程序实现字段级和业务级逻辑。 除了创建单独的 BLL 之外,如本教程中所述,另一个选项是通过使用分部类扩展 TableAdapters 的方法。 但是,使用此方法不允许我们替代现有方法,也不能像本文中采用的方法那样干净地分隔 DAL 和 BLL。
DAL 和 BLL 完成后,我们就可以开始演示层了。 在下一教程中,我们将对数据访问主题进行简短的绕道,并定义一致的页面布局供整个教程使用。
编程快乐!
关于作者
斯科特·米切尔是七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自 1998 年以来一直在使用 Microsoft Web 技术。 Scott 担任独立顾问、培训师和作家。 他的最新一本书是 山姆斯在 24 小时内 ASP.NET 2.0。 可以在 上mitchell@4GuysFromRolla.com联系他,也可以通过他的博客(可在 中找到http://ScottOnWriting.NET)。
特别感谢
本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是 Liz Shulok、Dennis Patterson、Carlos Santos 和 Hilton Giesenow。 有兴趣查看我即将发布的 MSDN 文章? 如果是,请在 处mitchell@4GuysFromRolla.com放置一行。