Création d’une couche de logique métier (VB)
par Scott Mitchell
Dans ce tutoriel, nous allons voir comment centraliser vos règles métier dans une couche logique métier (BLL) qui sert d’intermédiaire pour l’échange de données entre la couche de présentation et le DAL.
Introduction
La couche d’accès aux données (DAL) créée dans le premier tutoriel sépare proprement la logique d’accès aux données de la logique de présentation. Toutefois, bien que le DAL sépare proprement les détails d’accès aux données de la couche de présentation, il n’applique pas de règles métier qui peuvent s’appliquer. Par exemple, pour notre application, nous pouvons interdire la CategoryID
modification des champs ou SupplierID
de la Products
table lorsque le Discontinued
champ est défini sur 1, ou nous pourrions vouloir appliquer des règles d’ancienneté, en interdisant les situations dans lesquelles un employé est géré par une personne qui a été embauchée après lui. Un autre scénario courant est l’autorisation peut-être que seuls les utilisateurs d’un rôle particulier peuvent supprimer des produits ou modifier la UnitPrice
valeur.
Dans ce tutoriel, nous allons voir comment centraliser ces règles métier dans une couche logique métier (BLL) qui sert d’intermédiaire pour l’échange de données entre la couche de présentation et le DAL. Dans une application réelle, le BLL doit être implémenté en tant que projet de bibliothèque de classes distinct ; Toutefois, pour ces tutoriels, nous allons implémenter le BLL sous forme de série de classes dans notre App_Code
dossier afin de simplifier la structure du projet. La figure 1 illustre les relations architecturales entre la couche de présentation, BLL et DAL.
Figure 1 : La BLL sépare la couche de présentation de la couche d’accès aux données et impose des règles métier
Plutôt que de créer des classes distinctes pour implémenter notre logique métier, nous pourrions également placer cette logique directement dans le DataSet typé avec des classes partielles. Pour obtenir un exemple de création et d’extension d’un DataSet typé, reportez-vous au premier tutoriel.
Étape 1 : Création des classes BLL
Notre BLL sera composé de quatre classes, une pour chaque TableAdapter dans le DAL ; chacune de ces classes BLL aura des méthodes pour récupérer, insérer, mettre à jour et supprimer du TableAdapter respectif dans le DAL, en appliquant les règles métier appropriées.
Pour séparer plus proprement les classes DAL et BLL, nous allons créer deux sous-dossiers dans le App_Code
dossier, DAL
et BLL
. Cliquez simplement avec le bouton droit sur le App_Code
dossier dans le Explorateur de solutions et choisissez Nouveau dossier. Après avoir créé ces deux dossiers, déplacez le DataSet typé créé dans le premier tutoriel dans le DAL
sous-dossier.
Ensuite, créez les quatre fichiers de classe BLL dans le BLL
sous-dossier. Pour ce faire, cliquez avec le bouton droit sur le BLL
sous-dossier, choisissez Ajouter un nouvel élément, puis choisissez le modèle Classe. Nommez les quatre classes ProductsBLL
, CategoriesBLL
, SuppliersBLL
et EmployeesBLL
.
Figure 2 : Ajouter quatre nouvelles classes au App_Code
dossier
Ensuite, nous allons ajouter des méthodes à chacune des classes pour simplement encapsuler les méthodes définies pour les TableAdapters du premier tutoriel. Pour l’instant, ces méthodes appellent simplement directement dans le DAL ; nous reviendrons plus tard pour ajouter toute logique métier nécessaire.
Notes
Si vous utilisez Visual Studio Standard Edition ou une version ultérieure (autrement dit, vous n’utilisez pas Visual Web Developer), vous pouvez éventuellement concevoir vos classes visuellement à l’aide de l’Designer de classe. Pour plus d’informations sur cette nouvelle fonctionnalité dans Visual Studio, reportez-vous au blog class Designer.
Pour la ProductsBLL
classe, nous devons ajouter un total de sept méthodes :
GetProducts()
retourne tous les produitsGetProductByProductID(productID)
retourne le produit avec l’ID de produit spécifiéGetProductsByCategoryID(categoryID)
retourne tous les produits de la catégorie spécifiéeGetProductsBySupplier(supplierID)
retourne tous les produits du fournisseur spécifiéAddProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued)
insère un nouveau produit dans la base de données à l’aide des valeurs transmises ; retourne laProductID
valeur de l’enregistrement nouvellement inséréUpdateProduct(productName, supplierID, categoryID, quantityPerUnit, unitPrice, unitsInStock, unitsOnOrder, reorderLevel, discontinued, productID)
met à jour un produit existant dans la base de données à l’aide des valeurs transmises ; retourneTrue
si précisément une ligne a été mise à jour,False
sinonDeleteProduct(productID)
supprime le produit spécifié de la base de données
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
Les méthodes qui retournent simplement des données GetProducts
, GetProductByProductID
, GetProductsByCategoryID
et GetProductBySuppliersID
sont assez simples, car elles appellent simplement vers le bas dans le DAL. Bien que dans certains scénarios, il peut y avoir des règles métier qui doivent être implémentées à ce niveau (telles que des règles d’autorisation basées sur l’utilisateur actuellement connecté ou le rôle auquel appartient l’utilisateur), nous allons simplement laisser ces méthodes telles quelles. Pour ces méthodes, le BLL sert simplement de proxy par le biais duquel la couche de présentation accède aux données sous-jacentes à partir de la couche d’accès aux données.
Les AddProduct
méthodes et UpdateProduct
prennent toutes deux comme paramètres les valeurs des différents champs de produit et ajoutent un nouveau produit ou mettent à jour un produit existant, respectivement. Étant donné que de nombreuses colonnes de la Product
table peuvent accepter NULL
des valeurs (CategoryID
, SupplierID
, et UnitPrice
, pour n’en nommer que quelques-unes), ces paramètres d’entrée pour AddProduct
et UpdateProduct
qui mappent à ces colonnes utilisent des types nullables. Les types nullables sont des nouveautés dans .NET 2.0 et fournissent une technique permettant d’indiquer si un type de valeur doit plutôt être Nothing
. Pour plus d’informations, reportez-vous à l’article de blog de Paul VickThe Truth About Nullable Types and VB et à la documentation technique sur la structure Nullable .
Les trois méthodes retournent une valeur booléenne indiquant si une ligne a été insérée, mise à jour ou supprimée, car l’opération peut ne pas aboutir à une ligne affectée. Par exemple, si le développeur de page appelle DeleteProduct
en passant un ProductID
pour un produit inexistant, l’instruction DELETE
émise à la base de données n’aura aucun effet et la méthode retournera False
donc DeleteProduct
.
Notez que lors de l’ajout d’un nouveau produit ou de la mise à jour d’un produit existant, nous prenons les valeurs de champ du produit nouveau ou modifié comme une liste de scalaires, au lieu d’accepter une ProductsRow
instance. Cette approche a été choisie car la ProductsRow
classe dérive de la classe ADO.NET DataRow
, qui n’a pas de constructeur sans paramètre par défaut. Pour créer un ProductsRow
instance, nous devons d’abord créer un ProductsDataTable
instance, puis appeler sa NewProductRow()
méthode (ce que nous faisons dans AddProduct
). Cette lacune est en arrière lorsque nous nous tournons vers l’insertion et la mise à jour de produits à l’aide de ObjectDataSource. En bref, ObjectDataSource essaiera de créer une instance des paramètres d’entrée. Si la méthode BLL s’attend à un ProductsRow
instance, ObjectDataSource tente d’en créer un, mais échoue en raison de l’absence d’un constructeur sans paramètre par défaut. Pour plus d’informations sur ce problème, reportez-vous aux deux billets de forums ASP.NET suivants : Mise à jour d’ObjectDataSources avec Strongly-Typed DataSets et Problème avec ObjectDataSource et Strongly-Typed DataSet.
Ensuite, dans et AddProduct
UpdateProduct
, le code crée un ProductsRow
instance et le remplit avec les valeurs qui viennent d’être transmises. Lors de l’affectation de valeurs à DataColumns d’un DataRow, différentes vérifications de validation au niveau du champ peuvent se produire. Par conséquent, le fait de replacer manuellement les valeurs passées dans un DataRow permet de garantir la validité des données transmises à la méthode BLL. Malheureusement, les classes DataRow fortement typées générées par Visual Studio n’utilisent pas de types nullables. Au lieu de cela, pour indiquer qu’un DataColumn particulier dans un DataRow doit correspondre à une NULL
valeur de base de données, nous devons utiliser la SetColumnNameNull()
méthode .
Dans , UpdateProduct
nous chargeons d’abord dans le produit pour mettre à jour à l’aide de GetProductByProductID(productID)
. Bien que cela puisse sembler être un déplacement inutile dans la base de données, ce voyage supplémentaire s’avérera utile dans les prochains tutoriels qui explorent l’accès concurrentiel optimiste. La concurrence optimiste est une technique qui permet de s’assurer que deux utilisateurs qui travaillent simultanément sur les mêmes données ne remplacent pas accidentellement les modifications de l’autre. L’obtention de l’enregistrement entier facilite également la création de méthodes de mise à jour dans le BLL qui modifient uniquement un sous-ensemble des colonnes de DataRow. Lorsque nous explorons la SuppliersBLL
classe, nous voyons un tel exemple.
Enfin, notez que l’attribut DataObject est appliqué à la ProductsBLL
classe (syntaxe [System.ComponentModel.DataObject]
juste avant l’instruction de classe située en haut du fichier) et que les méthodes ont des attributs DataObjectMethodAttribute. L’attribut DataObject
marque la classe comme étant un objet approprié pour la liaison à un contrôle ObjectDataSource, tandis que le DataObjectMethodAttribute
indique l’objectif de la méthode. Comme nous le verrons dans les prochains tutoriels, ObjectDataSource de ASP.NET 2.0 permet d’accéder facilement aux données d’une classe de manière déclarative. Pour vous aider à filtrer la liste des classes possibles à lier dans l’Assistant ObjectDataSource, par défaut, seules les classes marquées comme DataObjects
sont indiquées dans la liste déroulante de l’Assistant. La ProductsBLL
classe fonctionnera tout aussi bien sans ces attributs, mais leur ajout facilite l’utilisation dans l’Assistant ObjectDataSource.
Ajout des autres classes
Une fois la ProductsBLL
classe terminée, nous devons toujours ajouter les classes pour travailler avec les catégories, les fournisseurs et les employés. Prenez un moment pour créer les classes et méthodes suivantes à l’aide des concepts de l’exemple ci-dessus :
CategoriesBLL.cs
GetCategories()
GetCategoryByCategoryID(categoryID)
SuppliersBLL.cs
GetSuppliers()
GetSupplierBySupplierID(supplierID)
GetSuppliersByCountry(country)
UpdateSupplierAddress(supplierID, address, city, country)
EmployeesBLL.cs
GetEmployees()
GetEmployeeByEmployeeID(employeeID)
GetEmployeesByManager(managerID)
La seule méthode à noter est la méthode de la SuppliersBLL
UpdateSupplierAddress
classe. Cette méthode fournit une interface permettant de mettre à jour uniquement les informations d’adresse du fournisseur. En interne, cette méthode lit dans l’objet SupplierDataRow
pour le spécifié supplierID
(à l’aide GetSupplierBySupplierID
de ), définit ses propriétés liées à l’adresse, puis appelle la SupplierDataTable
méthode de Update
. La UpdateSupplierAddress
méthode est la suivante :
<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
Reportez-vous au téléchargement de cet article pour connaître mon implémentation complète des classes BLL.
Étape 2 : Accès aux DataSets typés via les classes BLL
Dans le premier tutoriel, nous avons vu des exemples d’utilisation directe du DataSet typé par programmation, mais avec l’ajout de nos classes BLL, le niveau de présentation doit plutôt fonctionner sur le BLL. Dans l’exemple AllProducts.aspx
du premier tutoriel, le ProductsTableAdapter
a été utilisé pour lier la liste des produits à un GridView, comme indiqué dans le code suivant :
Dim productsAdapter As New ProductsTableAdapter()
GridView1.DataSource = productsAdapter.GetProducts()
GridView1.DataBind()
Pour utiliser les nouvelles classes BLL, il suffit de modifier la première ligne de code en remplaçant simplement l’objet ProductsTableAdapter
par un ProductBLL
objet :
Dim productLogic As New ProductsBLL()
GridView1.DataSource = productLogic.GetProducts()
GridView1.DataBind()
Les classes BLL sont également accessibles de manière déclarative (tout comme le DataSet typé) à l’aide de ObjectDataSource. Nous aborderons l’ObjetDataSource plus en détail dans les tutoriels suivants.
Figure 3 : La liste des produits s’affiche dans un GridView (cliquer pour afficher une image en taille réelle)
Étape 3 : Ajout de Field-Level validation aux classes DataRow
La validation au niveau du champ est des vérifications qui se rapportent aux valeurs de propriété des objets métier lors de l’insertion ou de la mise à jour. Voici quelques règles de validation au niveau du champ pour les produits :
- Le
ProductName
champ doit contenir 40 caractères ou moins - Le
QuantityPerUnit
champ doit contenir 20 caractères ou moins - Les
ProductID
champs ,ProductName
etDiscontinued
sont obligatoires, mais tous les autres champs sont facultatifs - Les
UnitPrice
champs ,UnitsInStock
,UnitsOnOrder
etReorderLevel
doivent être supérieurs ou égaux à zéro
Ces règles peuvent et doivent être exprimées au niveau de la base de données. La limite de caractères sur les ProductName
champs et QuantityPerUnit
est capturée par les types de données de ces colonnes dans la Products
table (nvarchar(40)
et nvarchar(20)
, respectivement). Si les champs sont obligatoires et facultatifs sont exprimés par si la colonne de la table de base de données autorise NULL
s. Il existe quatre contraintes case activée qui garantissent que seules les valeurs supérieures ou égales à zéro peuvent être intégrées aux UnitPrice
colonnes , UnitsInStock
, UnitsOnOrder
ou ReorderLevel
.
Outre l’application de ces règles au niveau de la base de données, elles doivent également être appliquées au niveau du DataSet. En fait, la longueur du champ et si une valeur est requise ou facultative sont déjà capturées pour chaque jeu dataTable de DataColumns. Pour voir la validation au niveau du champ fournie automatiquement, accédez à l’Designer DataSet, sélectionnez un champ dans l’un des DataTables, puis accédez au Fenêtre Propriétés. Comme le montre la figure 4, le QuantityPerUnit
DataColumn dans a ProductsDataTable
une longueur maximale de 20 caractères et autorise NULL
les valeurs. Si nous essayons de définir la ProductsDataRow
propriété sur une valeur de QuantityPerUnit
chaîne supérieure à 20 caractères, un ArgumentException
sera levée.
Figure 4 : DataColumn fournit la validation Field-Level de base (cliquez pour afficher l’image en taille réelle)
Malheureusement, nous ne pouvons pas spécifier de vérifications de limites, telles que la UnitPrice
valeur doit être supérieure ou égale à zéro, via le Fenêtre Propriétés. Pour fournir ce type de validation au niveau du champ, nous devons créer un gestionnaire d’événements pour l’événement ColumnChanging de DataTable. Comme mentionné dans le tutoriel précédent, les objets DataSet, DataTables et DataRow créés par le DataSet typé peuvent être étendus à l’aide de classes partielles. À l’aide de cette technique, nous pouvons créer un gestionnaire d’événements ColumnChanging
pour la ProductsDataTable
classe . Commencez par créer une classe dans le App_Code
dossier nommé ProductsDataTable.ColumnChanging.vb
.
Figure 5 : Ajouter une nouvelle classe au App_Code
dossier (cliquer pour afficher l’image en taille réelle)
Ensuite, créez un gestionnaire d’événements pour l’événement ColumnChanging
qui garantit que les UnitPrice
valeurs de colonne , UnitsInStock
, UnitsOnOrder
et ReorderLevel
(si ce n’est pas NULL
le cas ) sont supérieures ou égales à zéro. Si l’une de ces colonnes est hors limites, lèvez un 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
Étape 4 : Ajout de règles d’entreprise personnalisées aux classes du BLL
Outre la validation au niveau du champ, il peut exister des règles métier personnalisées de haut niveau qui impliquent différentes entités ou concepts non expressibles au niveau d’une seule colonne, par exemple :
- Si un produit est arrêté, son
UnitPrice
ne peut pas être mis à jour - Le pays de résidence d’un employé doit être le même que le pays de résidence de son responsable
- Un produit ne peut pas être abandonné s’il s’agit du seul produit fourni par le fournisseur
Les classes BLL doivent contenir des vérifications pour garantir le respect des règles métier de l’application. Ces vérifications peuvent être ajoutées directement aux méthodes auxquelles elles s’appliquent.
Imaginez que nos règles d’entreprise imposent qu’un produit ne puisse pas être marqué comme étant abandonné s’il était le seul produit d’un fournisseur donné. Autrement dit, si le produit X était le seul produit que nous avons acheté auprès du fournisseur Y, nous ne pourrions pas marquer X comme étant abandonné ; si, cependant, le fournisseur Y nous a fourni trois produits, A, B, et C, alors nous pourrions marquer n’importe lequel et tous ceux-ci comme abandonnés. Une règle d’entreprise étrange, mais les règles d’entreprise et le bon sens ne sont pas toujours alignés !
Pour appliquer cette règle métier dans la UpdateProducts
méthode, nous allons commencer par vérifier si Discontinued
a été défini True
sur et, si c’est le cas, nous appelons GetProductsBySupplierID
pour déterminer le nombre de produits que nous avons achetés auprès du fournisseur de ce produit. Si un seul produit est acheté auprès de ce fournisseur, nous lèveons un 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
Réponse aux erreurs de validation dans le niveau présentation
Lors de l’appel de la BLL à partir du niveau de présentation, nous pouvons décider s’il faut tenter de gérer les exceptions qui peuvent être déclenchées ou les laisser buller jusqu’à ASP.NET (ce qui déclenche l’événement HttpApplication
de Error
). Pour gérer une exception lors de l’utilisation du BLL par programmation, nous pouvons utiliser un try... Bloc catch , comme le montre l’exemple suivant :
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
Comme nous le verrons dans les prochains tutoriels, la gestion des exceptions qui s’affichent à partir de la BLL lors de l’utilisation d’un contrôle Web de données pour l’insertion, la mise à jour ou la suppression de données peut être gérée directement dans un gestionnaire d’événements au lieu d’avoir à inclure du code dans Try...Catch
des blocs.
Résumé
Une application bien conçue est conçue en couches distinctes, chacune encapsulant un rôle particulier. Dans le premier tutoriel de cette série d’articles, nous avons créé une couche d’accès aux données à l’aide de datasets typés ; Dans ce tutoriel, nous avons créé une couche de logique métier sous la forme d’une série de classes dans le dossier de App_Code
notre application qui appellent notre dal. Le BLL implémente la logique au niveau du champ et au niveau de l’entreprise pour notre application. En plus de créer une BLL distincte, comme nous l’avons fait dans ce tutoriel, une autre option consiste à étendre les méthodes de TableAdapters en utilisant des classes partielles. Toutefois, l’utilisation de cette technique ne nous permet pas de remplacer les méthodes existantes et ne sépare pas notre DAL et notre BLL aussi proprement que l’approche que nous avons adoptée dans cet article.
Une fois le DAL et le BLL terminés, nous sommes prêts à commencer sur notre couche de présentation. Dans le tutoriel suivant , nous allons faire un bref détour par les rubriques d’accès aux données et définir une mise en page cohérente à utiliser tout au long des didacticiels.
Bonne programmation !
À propos de l’auteur
Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille comme consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Il est accessible à l’adressemitchell@4GuysFromRolla.com . ou via son blog, qui se trouve à l’adresse http://ScottOnWriting.NET.
Remerciements spéciaux à
Cette série de tutoriels a été examinée par de nombreux réviseurs utiles. Les réviseurs principaux de ce tutoriel étaient Liz Shulok, Dennis Patterson, Carlos Santos et Hilton Giesenow. Vous souhaitez consulter mes prochains articles MSDN ? Si c’est le cas, déposez-moi une ligne à mitchell@4GuysFromRolla.com.