使用服务层进行验证 (VB)
了解如何将验证逻辑从控制器操作移动到单独的服务层中。 在本教程中,Stephen Walther 介绍了如何通过将服务层与控制器层隔离来保持关注点的清晰分离。
本教程的目的是介绍在 ASP.NET MVC 应用程序中执行验证的一种方法。 本教程介绍如何将验证逻辑从控制器移出并移动到单独的服务层中。
分离关注点
生成 ASP.NET MVC 应用程序时,不应将数据库逻辑置于控制器操作中。 混合使用数据库和控制器逻辑会使应用程序在一段时间内更难维护。 建议将所有数据库逻辑放在单独的存储库层中。
例如,列表 1 包含名为 ProductRepository 的简单存储库。 产品存储库包含应用程序的所有数据访问代码。 该列表还包括产品存储库实现的 IProductRepository 接口。
列表 1 - Models\ProductRepository.vb
Public Class ProductRepository
Implements IProductRepository
Private _entities As New ProductDBEntities()
Public Function ListProducts() As IEnumerable(Of Product) Implements IProductRepository.ListProducts
Return _entities.ProductSet.ToList()
End Function
Public Function CreateProduct(ByVal productToCreate As Product) As Boolean Implements IProductRepository.CreateProduct
Try
_entities.AddToProductSet(productToCreate)
_entities.SaveChanges()
Return True
Catch
Return False
End Try
End Function
End Class
Public Interface IProductRepository
Function CreateProduct(ByVal productToCreate As Product) As Boolean
Function ListProducts() As IEnumerable(Of Product)
End Interface
清单 2 中的控制器在其 Index () 和 Create () 操作中使用存储库层。 请注意,此控制器不包含任何数据库逻辑。 创建存储库层可以保持关注点的干净分离。 控制器负责应用程序流控制逻辑,存储库负责数据访问逻辑。
清单 2 - Controllers\ProductController.vb
Public Class ProductController
Inherits Controller
Private _repository As IProductRepository
Public Sub New()
Me.New(New ProductRepository())
End Sub
Public Sub New(ByVal repository As IProductRepository)
_repository = repository
End Sub
Public Function Index() As ActionResult
Return View(_repository.ListProducts())
End Function
'
' GET: /Product/Create
Public Function Create() As ActionResult
Return View()
End Function
'
' POST: /Product/Create
<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude:="Id")> ByVal productToCreate As Product) As ActionResult
_repository.CreateProduct(productToCreate)
Return RedirectToAction("Index")
End Function
End Class
创建服务层
因此,应用程序流控制逻辑属于控制器,数据访问逻辑属于存储库。 在这种情况下,将验证逻辑放在何处? 一个选项是将验证逻辑放在 服务层中。
服务层是 ASP.NET MVC 应用程序中的附加层,用于调解控制器层和存储库层之间的通信。 服务层包含业务逻辑。 具体而言,它包含验证逻辑。
例如,清单 3 中的产品服务层具有 CreateProduct () 方法。 CreateProduct () 方法调用 ValidateProduct () 方法来验证新产品,然后再将产品传递到产品存储库。
清单 3 - Models\ProductService.vb
Public Class ProductService
Implements IProductService
Private _modelState As ModelStateDictionary
Private _repository As IProductRepository
Public Sub New(ByVal modelState As ModelStateDictionary, ByVal repository As IProductRepository)
_modelState = modelState
_repository = repository
End Sub
Protected Function ValidateProduct(ByVal productToValidate As Product) As Boolean
If productToValidate.Name.Trim().Length = 0 Then
_modelState.AddModelError("Name", "Name is required.")
End If
If productToValidate.Description.Trim().Length = 0 Then
_modelState.AddModelError("Description", "Description is required.")
End If
If productToValidate.UnitsInStock
产品控制器已在清单 4 中更新为使用服务层而不是存储库层。 控制器层与服务层通信。 服务层与存储库层通信。 每个层都有单独的责任。
清单 4 - Controllers\ProductController.vb
Public Class ProductController
Inherits Controller
Private _service As IProductService
Public Sub New()
_service = New ProductService(Me.ModelState, New ProductRepository())
End Sub
Public Sub New(ByVal service As IProductService)
_service = service
End Sub
Public Function Index() As ActionResult
Return View(_service.ListProducts())
End Function
'
' GET: /Product/Create
Public Function Create() As ActionResult
Return View()
End Function
'
' POST: /Product/Create
<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude := "Id")> ByVal productToCreate As Product) As ActionResult
If Not _service.CreateProduct(productToCreate) Then
Return View()
End If
Return RedirectToAction("Index")
End Function
End Class
请注意,产品服务是在产品控制器构造函数中创建的。 创建产品服务时,模型状态字典将传递给服务。 产品服务使用模型状态将验证错误消息传递回控制器。
分离服务层
我们未能在一个方面隔离控制器和服务层。 控制器和服务层通过模型状态进行通信。 换句话说,服务层依赖于 ASP.NET MVC 框架的特定功能。
我们希望尽可能将服务层与控制器层隔离开来。 从理论上讲,我们应该能够将服务层用于任何类型的应用程序,而不仅仅是 ASP.NET MVC 应用程序。 例如,将来,我们可能需要为应用程序生成 WPF 前端。 我们应该找到一种方法,从服务层中删除对 ASP.NET MVC 模型状态的依赖关系。
在清单 5 中,服务层已更新,使其不再使用模型状态。 相反,它使用实现 IValidationDictionary 接口的任何类。
清单 5 - Models\ProductService.vb (分离)
Public Class ProductService
Implements IProductService
Private _validatonDictionary As IValidationDictionary
Private _repository As IProductRepository
Public Sub New(ByVal validationDictionary As IValidationDictionary, ByVal repository As IProductRepository)
_validatonDictionary = validationDictionary
_repository = repository
End Sub
Protected Function ValidateProduct(ByVal productToValidate As Product) As Boolean
If productToValidate.Name.Trim().Length = 0 Then
_validatonDictionary.AddError("Name", "Name is required.")
End If
If productToValidate.Description.Trim().Length = 0 Then
_validatonDictionary.AddError("Description", "Description is required.")
End If
If productToValidate.UnitsInStock
IValidationDictionary 接口在清单 6 中定义。 此简单接口具有单个方法和单个属性。
清单 6 - Models\IValidationDictionary.cs
Public Interface IValidationDictionary
Sub AddError(ByVal key As String, ByVal errorMessage As String)
ReadOnly Property IsValid() As Boolean
End Interface
清单 7 中的类(名为 ModelStateWrapper 类)实现 IValidationDictionary 接口。 可以通过将模型状态字典传递给构造函数来实例化 ModelStateWrapper 类。
清单 7 - Models\ModelStateWrapper.vb
Public Class ModelStateWrapper
Implements IValidationDictionary
Private _modelState As ModelStateDictionary
Public Sub New(ByVal modelState As ModelStateDictionary)
_modelState = modelState
End Sub
#Region "IValidationDictionary Members"
Public Sub AddError(ByVal key As String, ByVal errorMessage As String) Implements IValidationDictionary.AddError
_modelState.AddModelError(key, errorMessage)
End Sub
Public ReadOnly Property IsValid() As Boolean Implements IValidationDictionary.IsValid
Get
Return _modelState.IsValid
End Get
End Property
#End Region
End Class
最后,清单 8 中更新的控制器在其构造函数中创建服务层时使用 ModelStateWrapper。
清单 8 - Controllers\ProductController.vb
Public Class ProductController
Inherits Controller
Private _service As IProductService
Public Sub New()
_service = New ProductService(New ModelStateWrapper(Me.ModelState), New ProductRepository())
End Sub
Public Sub New(ByVal service As IProductService)
_service = service
End Sub
Public Function Index() As ActionResult
Return View(_service.ListProducts())
End Function
'
' GET: /Product/Create
Public Function Create() As ActionResult
Return View()
End Function
'
' POST: /Product/Create
<AcceptVerbs(HttpVerbs.Post)> _
Public Function Create(<Bind(Exclude := "Id")> ByVal productToCreate As Product) As ActionResult
If Not _service.CreateProduct(productToCreate) Then
Return View()
End If
Return RedirectToAction("Index")
End Function
End Class
使用 IValidationDictionary 接口和 ModelStateWrapper 类可将服务层与控制器层完全隔离。 服务层不再依赖于模型状态。 可以将实现 IValidationDictionary 接口的任何类传递到服务层。 例如,WPF 应用程序可以使用简单的集合类实现 IValidationDictionary 接口。
总结
本教程的目的是讨论在 ASP.NET MVC 应用程序中执行验证的一种方法。 在本教程中,你已了解如何将所有验证逻辑从控制器移出并移动到单独的服务层。 你还了解了如何通过创建 ModelStateWrapper 类将服务层与控制器层隔离开来。