Partager via


Création d’une couche de logique métier (C#)

par Scott Mitchell

Télécharger le PDF

Dans ce tutoriel, nous allons voir comment centraliser vos règles métier dans une couche de 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é, 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 de 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 la BLL en tant que 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.

La BLL sépare la couche présentation de la couche d’accès aux données et impose des règles d’entreprise

Figure 1 : La BLL sépare la couche présentation de la couche d’accès aux données et impose des règles d’entreprise

É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 d’entreprise appropriées.

Pour séparer plus proprement les classes dal- et BLL, nous allons créer deux sous-dossiers dans le App_Code dossier et DALBLL. 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 didacticiel 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, SuppliersBLLet EmployeesBLL.

Ajouter quatre nouvelles classes au dossier App_Code

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 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 (c’est-à-dire que 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, consultez le blog class Designer.

Pour la ProductsBLL classe , nous devons ajouter un total de sept méthodes :

  • GetProducts() retourne tous les produits
  • GetProductByProductID(productID) retourne le produit avec l’ID de produit spécifié
  • GetProductsByCategoryID(categoryID) retourne tous les produits de la catégorie spécifiée
  • GetProductsBySupplier(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 passées ; retourne la ProductID 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 passées ; retourne true si précisément une ligne a été mise à jour ; sinon, false
  • DeleteProduct(productID) supprime le produit spécifié de la base de données

ProductsBLL.cs

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using NorthwindTableAdapters;

[System.ComponentModel.DataObject]
public class ProductsBLL
{
    private ProductsTableAdapter _productsAdapter = null;
    protected ProductsTableAdapter Adapter
    {
        get {
            if (_productsAdapter == null)
                _productsAdapter = new ProductsTableAdapter();

            return _productsAdapter;
        }
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, true)]
    public Northwind.ProductsDataTable GetProducts()
    {
        return Adapter.GetProducts();
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductByProductID(int productID)
    {
        return Adapter.GetProductByProductID(productID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsByCategoryID(int categoryID)
    {
        return Adapter.GetProductsByCategoryID(categoryID);
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Select, false)]
    public Northwind.ProductsDataTable GetProductsBySupplierID(int supplierID)
    {
        return Adapter.GetProductsBySupplierID(supplierID);
    }
    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Insert, true)]
    public bool AddProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice,  short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued)
    {
        // Create a new ProductRow instance
        Northwind.ProductsDataTable products = new Northwind.ProductsDataTable();
        Northwind.ProductsRow product = products.NewProductsRow();

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Add the new product
        products.AddProductsRow(product);
        int rowsAffected = Adapter.Update(products);

        // Return true if precisely one row was inserted,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Update, true)]
    public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
        string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
        short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
    {
        Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
        if (products.Count == 0)
            // no matching record found, return false
            return false;

        Northwind.ProductsRow product = products[0];

        product.ProductName = productName;
        if (supplierID == null) product.SetSupplierIDNull();
          else product.SupplierID = supplierID.Value;
        if (categoryID == null) product.SetCategoryIDNull();
          else product.CategoryID = categoryID.Value;
        if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
          else product.QuantityPerUnit = quantityPerUnit;
        if (unitPrice == null) product.SetUnitPriceNull();
          else product.UnitPrice = unitPrice.Value;
        if (unitsInStock == null) product.SetUnitsInStockNull();
          else product.UnitsInStock = unitsInStock.Value;
        if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
          else product.UnitsOnOrder = unitsOnOrder.Value;
        if (reorderLevel == null) product.SetReorderLevelNull();
          else product.ReorderLevel = reorderLevel.Value;
        product.Discontinued = discontinued;

        // Update the product record
        int rowsAffected = Adapter.Update(product);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }

    [System.ComponentModel.DataObjectMethodAttribute
        (System.ComponentModel.DataObjectMethodType.Delete, true)]
    public bool DeleteProduct(int productID)
    {
        int rowsAffected = Adapter.Delete(productID);

        // Return true if precisely one row was deleted,
        // otherwise false
        return rowsAffected == 1;
    }
}

Les méthodes qui retournent simplement des données GetProducts, GetProductByProductID, GetProductsByCategoryIDet GetProductBySuppliersID sont assez simples, car elles appellent simplement le dal. Alors que dans certains scénarios, il peut y avoir des règles d’entreprise 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 alors 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 en tant que 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, SupplierIDet 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 nouveaux dans .NET 2.0 et fournissent une technique permettant d’indiquer si un type valeur doit être null. En C#, vous pouvez marquer un type valeur comme un type nullable en ajoutant ? après le type (comme int? x;). Pour plus d’informations, consultez la section Types nullables du Guide de programmation C #.

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 entraîner une ligne affectée. Par exemple, si le développeur de la page appelle DeleteProduct le passage d’un ProductID pour un produit inexistant, l’instruction DELETE émise pour la base de données n’aura aucun effet et, par conséquent, la DeleteProduct méthode retournera false.

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 un 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 se présente lorsque nous nous tournons vers l’insertion et la mise à jour de produits à l’aide d’ObjectDataSource. En bref, ObjectDataSource essaiera de créer une instance des paramètres d’entrée. Si la méthode BLL 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 AddProduct et UpdateProduct, le code crée un ProductsRow instance et le remplit avec les valeurs que vous venez de passer. 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 passées à 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 le produit à mettre à jour à l’aide de GetProductByProductID(productID). Bien que cela puisse sembler un voyage inutile vers la base de données, ce voyage supplémentaire s’avérera utile dans les prochains tutoriels qui explorent l’accès concurrentiel optimiste. L’accès concurrentiel 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 la BLL qui ne modifient qu’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 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, l’objet ObjectDataSource de ASP.NET 2.0 facilite l’accès déclaratif aux données à partir d’une classe. Pour faciliter le filtrage de la liste des classes possibles à lier dans l’Assistant ObjectDataSource, par défaut, seules les classes marquées comme DataObjects sont affiché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 méthode de la classe est la méthode de UpdateSupplierAddress la SuppliersBLL classe . Cette méthode fournit une interface permettant de mettre à jour uniquement les informations d’adresse du fournisseur. En interne, cette méthode lit l’objet pour le SupplierDataRow spécifié supplierID (à l’aide GetSupplierBySupplierIDde ), définit ses propriétés liées à l’adresse, puis appelle la SupplierDataTableméthode de Update . La UpdateSupplierAddress méthode est la suivante :

[System.ComponentModel.DataObjectMethodAttribute
    (System.ComponentModel.DataObjectMethodType.Update, true)]
public bool UpdateSupplierAddress
    (int supplierID, string address, string city, string country)
{
    Northwind.SuppliersDataTable suppliers =
        Adapter.GetSupplierBySupplierID(supplierID);
    if (suppliers.Count == 0)
        // no matching record found, return false
        return false;
    else
    {
        Northwind.SuppliersRow supplier = suppliers[0];

        if (address == null) supplier.SetAddressNull();
          else supplier.Address = address;
        if (city == null) supplier.SetCityNull();
          else supplier.City = city;
        if (country == null) supplier.SetCountryNull();
          else supplier.Country = country;

        // Update the supplier Address-related information
        int rowsAffected = Adapter.Update(supplier);

        // Return true if precisely one row was updated,
        // otherwise false
        return rowsAffected == 1;
    }
}

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 présentation doit fonctionner par rapport au fichier BLL à la place. 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 :

ProductsTableAdapter productsAdapter = new ProductsTableAdapter();
GridView1.DataSource = productsAdapter.GetProducts();
GridView1.DataBind();

Pour utiliser les nouvelles classes BLL, il suffit de modifier la première ligne de code pour remplacer simplement l’objet ProductsTableAdapter par un ProductBLL objet :

ProductsBLL productLogic = 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 allons aborder l’ObjetDataSource plus en détail dans les didacticiels suivants.

La liste des produits est affichée dans un GridView

Figure 3 : La liste des produits est affichée 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 :

  • La longueur du ProductName champ doit être inférieure ou inférieure à 40 caractères
  • La longueur du QuantityPerUnit champ doit être inférieure ou inférieure à 20 caractères
  • Les ProductIDchamps , ProductNameet Discontinued sont obligatoires, mais tous les autres champs sont facultatifs
  • Les UnitPricechamps , UnitsInStock, UnitsOnOrderet ReorderLevel 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 UnitPricecolonnes , UnitsInStock, UnitsOnOrderou 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 ProductsDataRowpropriété sur une valeur de QuantityPerUnit chaîne supérieure à 20 caractères, un ArgumentException sera levée.

DataColumn fournit la validation Field-Level de base

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.cs.

Ajouter une nouvelle classe au dossier App_Code

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 UnitPricevaleurs de colonne , UnitsInStock, UnitsOnOrderet ReorderLevel (si ce n’est pas NULLle cas ) sont supérieures ou égales à zéro. Si l’une de ces colonnes est hors limites, lèvez un ArgumentException.

ProductsDataTable.ColumnChanging.cs

public partial class Northwind
{
    public partial class ProductsDataTable
    {
        public override void BeginInit()
         {
            this.ColumnChanging += ValidateColumn;
         }

         void ValidateColumn(object sender,
           DataColumnChangeEventArgs e)
         {
            if(e.Column.Equals(this.UnitPriceColumn))
            {
               if(!Convert.IsDBNull(e.ProposedValue) &&
                  (decimal)e.ProposedValue < 0)
               {
                  throw new ArgumentException(
                      "UnitPrice cannot be less than zero", "UnitPrice");
               }
            }
            else if (e.Column.Equals(this.UnitsInStockColumn) ||
                     e.Column.Equals(this.UnitsOnOrderColumn) ||
                     e.Column.Equals(this.ReorderLevelColumn))
            {
                if (!Convert.IsDBNull(e.ProposedValue) &&
                    (short)e.ProposedValue < 0)
                {
                    throw new ArgumentException(string.Format(
                        "{0} cannot be less than zero", e.Column.ColumnName),
                        e.Column.ColumnName);
                }
            }
         }
    }
}

É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.

public bool UpdateProduct(string productName, int? supplierID, int? categoryID,
    string quantityPerUnit, decimal? unitPrice, short? unitsInStock,
    short? unitsOnOrder, short? reorderLevel, bool discontinued, int productID)
{
    Northwind.ProductsDataTable products = Adapter.GetProductByProductID(productID);
    if (products.Count == 0)
        // no matching record found, return false
        return false;

    Northwind.ProductsRow product = products[0];

    // Business rule check - cannot discontinue
    // a product that is supplied by only
    // one supplier
    if (discontinued)
    {
        // Get the products we buy from this supplier
        Northwind.ProductsDataTable productsBySupplier =
            Adapter.GetProductsBySupplierID(product.SupplierID);

        if (productsBySupplier.Count == 1)
            // this is the only product we buy from this supplier
            throw new ApplicationException(
                "You cannot mark a product as discontinued if it is the only
                  product purchased from a supplier");
    }

    product.ProductName = productName;
    if (supplierID == null) product.SetSupplierIDNull();
      else product.SupplierID = supplierID.Value;
    if (categoryID == null) product.SetCategoryIDNull();
      else product.CategoryID = categoryID.Value;
    if (quantityPerUnit == null) product.SetQuantityPerUnitNull();
      else product.QuantityPerUnit = quantityPerUnit;
    if (unitPrice == null) product.SetUnitPriceNull();
      else product.UnitPrice = unitPrice.Value;
    if (unitsInStock == null) product.SetUnitsInStockNull();
      else product.UnitsInStock = unitsInStock.Value;
    if (unitsOnOrder == null) product.SetUnitsOnOrderNull();
      else product.UnitsOnOrder = unitsOnOrder.Value;
    if (reorderLevel == null) product.SetReorderLevelNull();
      else product.ReorderLevel = reorderLevel.Value;
    product.Discontinued = discontinued;

    // Update the product record
    int rowsAffected = Adapter.Update(product);

    // Return true if precisely one row was updated,
    // otherwise false
    return rowsAffected == 1;
}

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 HttpApplicationde Error ). Pour gérer une exception lors de l’utilisation du BLL par programmation, nous pouvons utiliser un try... catch block, comme le montre l’exemple suivant :

ProductsBLL productLogic = new ProductsBLL();

// Update information for ProductID 1
try
{
    // This will fail since we are attempting to use a
    // UnitPrice value less than 0.
    productLogic.UpdateProduct(
        "Scott s Tea", 1, 1, null, -14m, 10, null, null, false, 1);
}
catch (ArgumentException ae)
{
    Response.Write("There was a problem: " + ae.Message);
}

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.