Ověřování vrstvou služby (VB)
Zjistěte, jak přesunout logiku ověřování z akcí kontroleru do samostatné vrstvy služby. V tomto kurzu Stephen Walther vysvětluje, jak můžete zachovat ostré oddělení problémů tím, že izolujete vrstvu služeb od vrstvy kontroleru.
Cílem tohoto kurzu je popsat jednu metodu ověřování v aplikaci ASP.NET MVC. V tomto kurzu se naučíte přesunout logiku ověřování z kontrolerů do samostatné vrstvy služby.
Oddělení obav
Když vytváříte aplikaci ASP.NET MVC, neměli byste logiku databáze umisťovat do akcí kontroleru. Kombinování logiky databáze a kontroleru ztěžuje údržbu vaší aplikace v průběhu času. Doporučujeme umístit veškerou logiku databáze do samostatné vrstvy úložiště.
Například výpis 1 obsahuje jednoduché úložiště s názvem ProductRepository. Úložiště produktů obsahuje veškerý kód pro přístup k datům aplikace. Výpis obsahuje také rozhraní IProductRepository, které implementuje úložiště produktů.
Výpis 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
Kontroler ve výpisu 2 používá vrstvu úložiště v akcích Index() i Create(). Všimněte si, že tento kontroler neobsahuje žádnou logiku databáze. Vytvoření vrstvy úložiště vám umožní udržovat čisté oddělení oblastí zájmu. Kontrolery zodpovídají za logiku řízení toku aplikace a úložiště zodpovídá za logiku přístupu k datům.
Výpis 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
Vytvoření vrstvy služby
Logika řízení toku aplikace tedy patří do kontroleru a logika přístupu k datům patří do úložiště. Kam v takovém případě umístíte logiku ověřování? Jednou z možností je umístit logiku ověřování do vrstvy služby.
Vrstva služby je další vrstva v aplikaci ASP.NET MVC, která zprostředkováává komunikaci mezi kontrolerem a vrstvou úložiště. Vrstva služby obsahuje obchodní logiku. Konkrétně obsahuje logiku ověřování.
Například vrstva služeb produktů ve výpisu 3 má metodu CreateProduct(). Metoda CreateProduct() volá metodu ValidateProduct() k ověření nového produktu před předáním produktu do úložiště produktů.
Výpis 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
Kontroler produktu byl ve výpisu 4 aktualizován tak, aby místo vrstvy úložiště používal vrstvu služby. Vrstva kontroleru komunikuje s vrstvou služby. Vrstva služby komunikuje s vrstvou úložiště. Každá vrstva má samostatnou odpovědnost.
Výpis 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
Všimněte si, že služba produktu je vytvořená v konstruktoru kontroleru produktů. Po vytvoření služby produktu se službě předá slovník stavu modelu. Produktová služba používá stav modelu k předání chybových zpráv ověření zpět kontroleru.
Oddělení vrstvy služby
Nepodařilo se nám izolovat vrstvy kontroleru a služby v jednom ohledu. Vrstva kontroleru a služby komunikují prostřednictvím stavu modelu. Jinými slovy, vrstva služby závisí na konkrétní funkci architektury ASP.NET MVC.
Chceme co nejvíce izolovat vrstvu služby od vrstvy kontroleru. Teoreticky bychom měli být schopni použít vrstvu služby s libovolným typem aplikace, a ne jen s aplikací ASP.NET MVC. V budoucnu například můžeme chtít pro naši aplikaci vytvořit front-end WPF. Měli bychom najít způsob, jak odebrat závislost na ASP.NET stavu modelu MVC z naší vrstvy služby.
Ve výpisu 5 se vrstva služby aktualizovala tak, že už nepoužívá stav modelu. Místo toho používá jakoukoli třídu, která implementuje rozhraní IValidationDictionary.
Výpis 5 – Models\ProductService.vb (oddělený)
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
Rozhraní IValidationDictionary je definováno ve výpisu 6. Toto jednoduché rozhraní má jednu metodu a jednu vlastnost.
Výpis 6 – Models\IValidationDictionary.cs
Public Interface IValidationDictionary
Sub AddError(ByVal key As String, ByVal errorMessage As String)
ReadOnly Property IsValid() As Boolean
End Interface
Třída ve výpisu 7 s názvem ModelStateWrapper třída implementuje rozhraní IValidationDictionary. Instanci třídy ModelStateWrapper můžete vytvořit předáním slovníku stavu modelu konstruktoru.
Výpis 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
Aktualizovaný kontroler ve výpisu 8 používá ModelStateWrapper při vytváření vrstvy služby ve svém konstruktoru.
Výpis 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
Použití rozhraní IValidationDictionary a ModelStateWrapper třídy nám umožňuje zcela izolovat vrstvu služby od vrstvy kontroleru. Vrstva služby už není závislá na stavu modelu. Do vrstvy služby můžete předat jakoukoli třídu, která implementuje rozhraní IValidationDictionary. Například aplikace WPF může implementovat IValidationDictionary rozhraní s jednoduchou kolekcí třídy.
Souhrn
Cílem tohoto kurzu bylo prodiskutovat jeden přístup k ověřování v aplikaci ASP.NET MVC. V tomto kurzu jste zjistili, jak přesunout veškerou logiku ověřování z kontrolerů do samostatné vrstvy služby. Také jste zjistili, jak izolovat vrstvu služby od vrstvy kontroleru vytvořením třídy ModelStateWrapper.