Hinweis
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, sich anzumelden oder das Verzeichnis zu wechseln.
Für den Zugriff auf diese Seite ist eine Autorisierung erforderlich. Sie können versuchen, das Verzeichnis zu wechseln.
von Stephen Walther
Erfahren Sie, wie Sie Ihre Validierungslogik aus Ihren Controlleraktionen heraus in eine separate Dienstebene verschieben. In diesem Tutorial erläutert Stephen Walther, wie Sie eine scharfe Trennung der Bedenken beibehalten können, indem Sie Ihre Dienstebene von Ihrer Controllerebene isolieren.
Das Ziel dieses Tutorials besteht darin, eine Methode zum Durchführen der Validierung in einer ASP.NET MVC-Anwendung zu beschreiben. In diesem Tutorial erfahren Sie, wie Sie Ihre Validierungslogik aus Ihren Controllern in eine separate Dienstebene verschieben.
Trennen von Bedenken
Wenn Sie eine ASP.NET MVC-Anwendung erstellen, sollten Sie die Datenbanklogik nicht in Ihren Controlleraktionen platzieren. Das Mischen Ihrer Datenbank- und Controllerlogik erschwert die Verwaltung Ihrer Anwendung im Laufe der Zeit. Es wird empfohlen, die gesamte Datenbanklogik in einer separaten Repositoryebene zu platzieren.
Beispielsweise enthält Listing 1 ein einfaches Repository namens ProductRepository. Das Produktrepository enthält den gesamten Datenzugriffscode für die Anwendung. Die Auflistung enthält auch die IProductRepository-Schnittstelle, die vom Produktrepository implementiert wird.
Auflistung 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
Der Controller in Listing 2 verwendet die Repositoryebene sowohl in den Aktionen Index() als auch create(). Beachten Sie, dass dieser Controller keine Datenbanklogik enthält. Durch das Erstellen einer Repositoryebene können Sie eine sauber Trennung von Bedenken beibehalten. Controller sind für die Logik zur Steuerung des Anwendungsflusses verantwortlich, und das Repository ist für die Datenzugriffslogik verantwortlich.
Auflistung 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
Erstellen einer Dienstebene
Die Logik zur Steuerung des Anwendungsflusses gehört also in einen Controller, und die Datenzugriffslogik gehört in ein Repository. Wo legen Sie in diesem Fall Ihre Validierungslogik ein? Eine Möglichkeit besteht darin, Ihre Validierungslogik auf einer Dienstebene zu platzieren.
Eine Dienstebene ist eine zusätzliche Ebene in einer ASP.NET MVC-Anwendung, die die Kommunikation zwischen einem Controller und einer Repositoryebene vermittelt. Die Dienstebene enthält Geschäftslogik. Insbesondere enthält sie Validierungslogik.
Beispielsweise verfügt die Produktdienstebene in Listing 3 über eine CreateProduct()-Methode. Die CreateProduct()-Methode ruft die ValidateProduct()-Methode auf, um ein neues Produkt zu überprüfen, bevor das Produkt an das Produktrepository übergeben wird.
Auflistung 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
Der Produktcontroller wurde in Listing 4 aktualisiert, um die Dienstebene anstelle der Repositoryebene zu verwenden. Die Controllerebene kommuniziert mit der Dienstebene. Die Dienstebene kommuniziert mit der Repositoryebene. Jede Ebene hat eine eigene Verantwortung.
Auflistung 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
Beachten Sie, dass der Produktdienst im Produktcontrollerkonstruktor erstellt wird. Wenn der Produktdienst erstellt wird, wird das Modellzustandsverzeichnis an den Dienst übergeben. Der Produktdienst verwendet den Modellzustand, um Validierungsfehlermeldungen an den Controller zurückzugeben.
Entkoppeln der Dienstebene
Es ist nicht gelungen, die Controller- und Dienstebenen in einer Hinsicht zu isolieren. Die Controller- und Dienstebenen kommunizieren über den Modellzustand. Anders ausgedrückt: Die Dienstebene ist von einem bestimmten Feature des ASP.NET MVC-Frameworks abhängig.
Wir möchten die Dienstebene so weit wie möglich von unserer Controllerebene isolieren. Theoretisch sollten wir in der Lage sein, die Dienstebene mit jeder Art von Anwendung und nicht nur mit einer ASP.NET MVC-Anwendung zu verwenden. In Zukunft können wir beispielsweise ein WPF-Front-End für unsere Anwendung erstellen. Wir sollten eine Möglichkeit finden, die Abhängigkeit von ASP.NET MVC-Modellzustand aus unserer Dienstebene zu entfernen.
In Auflistung 5 wurde die Dienstebene aktualisiert, sodass sie den Modellzustand nicht mehr verwendet. Stattdessen werden alle Klassen verwendet, die die IValidationDictionary-Schnittstelle implementiert.
Auflistung 5: Models\ProductService.vb (entkoppelt)
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
Die IValidationDictionary-Schnittstelle ist in Listing 6 definiert. Diese einfache Schnittstelle verfügt über eine einzelne Methode und eine einzelne Eigenschaft.
Auflistung 6: Models\IValidationDictionary.cs
Public Interface IValidationDictionary
Sub AddError(ByVal key As String, ByVal errorMessage As String)
ReadOnly Property IsValid() As Boolean
End Interface
Die Klasse in Listing 7 mit dem Namen ModelStateWrapper-Klasse implementiert die IValidationDictionary-Schnittstelle. Sie können die ModelStateWrapper-Klasse instanziieren, indem Sie ein Modellzustandsverzeichnis an den Konstruktor übergeben.
Auflistung 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
Schließlich verwendet der aktualisierte Controller in Listing 8 den ModelStateWrapper, wenn die Dienstebene in ihrem Konstruktor erstellt wird.
Auflistung 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
Mithilfe der IValidationDictionary-Schnittstelle und der ModelStateWrapper-Klasse können wir die Dienstebene vollständig von der Controllerebene isolieren. Die Dienstebene ist nicht mehr vom Modellstatus abhängig. Sie können jede Klasse, die die IValidationDictionary-Schnittstelle implementiert, an die Dienstebene übergeben. Beispielsweise kann eine WPF-Anwendung die IValidationDictionary-Schnittstelle mit einer einfachen Sammlungsklasse implementieren.
Zusammenfassung
Das Ziel dieses Tutorials war es, einen Ansatz zum Durchführen der Validierung in einer ASP.NET MVC-Anwendung zu diskutieren. In diesem Tutorial haben Sie gelernt, wie Sie Ihre gesamte Validierungslogik aus Ihren Controllern in eine separate Dienstebene verschieben. Außerdem haben Sie gelernt, wie Sie Ihre Dienstebene von Der Controllerebene isolieren, indem Sie eine ModelStateWrapper-Klasse erstellen.