Partager via


Créer un modèle avec des validations de règles d’entreprise

par Microsoft

Télécharger le PDF

Il s’agit de l’étape 3 d’un didacticiel gratuit sur l’application « NerdDinner » qui explique comment créer une application web petite mais complète à l’aide de ASP.NET MVC 1.

L’étape 3 montre comment créer un modèle que nous pouvons utiliser pour interroger et mettre à jour la base de données pour notre application NerdDinner.

Si vous utilisez ASP.NET MVC 3, nous vous recommandons de suivre les didacticiels Prise en main Avec MVC 3 ou MVC Music Store.

Étape 3 de NerdDinner : génération du modèle

Dans une infrastructure de contrôleur de vue de modèle, le terme « modèle » fait référence aux objets qui représentent les données de l’application, ainsi qu’à la logique de domaine correspondante qui y intègre des règles de validation et d’entreprise. Le modèle est à bien des égards le « cœur » d’une application MVC et, comme nous le verrons plus tard, pilote fondamentalement son comportement.

L’infrastructure MVC ASP.NET prend en charge l’utilisation de n’importe quelle technologie d’accès aux données, et les développeurs peuvent choisir parmi diverses options de données .NET riches pour implémenter leurs modèles, notamment : LINQ to Entities, LINQ to SQL, NHibernate, LLBLGen Pro, SubSonic, WilsonORM, ou simplement ADO.NET DataReaders ou DataSets bruts.

Pour notre application NerdDinner, nous allons utiliser LINQ to SQL pour créer un modèle simple qui correspond assez étroitement à notre conception de base de données et ajoute des règles métier et logiques de validation personnalisées. Nous allons ensuite implémenter une classe de référentiel qui permet d’extraire l’implémentation de persistance des données du reste de l’application et de la tester facilement à l’unité.

LINQ to SQL

LINQ to SQL est un mappeur relationnel d’objet (ORM) fourni dans le cadre de .NET 3.5.

LINQ to SQL fournit un moyen simple de mapper des tables de base de données à des classes .NET sur lesquelles nous pouvons coder. Pour notre application NerdDinner, nous allons l’utiliser pour mapper les tables Dinners et RSVP de notre base de données aux classes Dinner et RSVP. Les colonnes des tables Dinners et RSVP correspondent aux propriétés des classes Dinner et RSVP. Chaque objet Dinner et RSVP représente une ligne distincte dans les tables Dinners ou RSVP de la base de données.

LINQ to SQL nous permet d’éviter d’avoir à construire manuellement des instructions SQL pour récupérer et mettre à jour des objets Dinner et RSVP avec des données de base de données. Au lieu de cela, nous allons définir les classes Dinner et RSVP, la façon dont elles sont mappées à la base de données et les relations entre elles. LINQ to SQL s’occupe ensuite de générer la logique d’exécution SQL appropriée à utiliser lors de l’exécution lorsque nous interagissons et les utilisons.

Nous pouvons utiliser la prise en charge du langage LINQ dans VB et C# pour écrire des requêtes expressives qui récupèrent des objets Dinner et RSVP de la base de données. Cela réduit la quantité de code de données que nous devons écrire et nous permet de créer des applications vraiment propre.

Ajout de classes LINQ to SQL à notre projet

Nous allons commencer par cliquer avec le bouton droit sur le dossier « Modèles » dans notre projet, puis sélectionner la commande de menu Ajouter un> nouvel élément :

Capture d’écran du dossier Models. Le nouvel élément est mis en surbrillance. Les modèles sont mis en surbrillance et sélectionnés.

La boîte de dialogue « Ajouter un nouvel élément » s’affiche. Nous allons filtrer par catégorie « Données » et sélectionner le modèle « classes LINQ to SQL » dans celle-ci :

Capture d’écran de la boîte de dialogue Ajouter un nouvel élément. Les données sont mises en surbrillance. Les classes L I N Q à QL sont sélectionnées et mises en surbrillance.

Nous allons nommer l’élément « NerdDinner », puis cliquer sur le bouton « Ajouter ». Visual Studio ajoute un fichier NerdDinner.dbml sous notre répertoire \Models, puis ouvre le concepteur relationnel d’objet LINQ to SQL :

Capture d’écran de la boîte de dialogue Nerd Dinner dans Visual Studio. Le fichier Nerd Dinner dot d b m l est sélectionné.

Création de classes de modèle de données avec LINQ to SQL

LINQ to SQL nous permet de créer rapidement des classes de modèle de données à partir d’un schéma de base de données existant. Pour ce faire, nous allons ouvrir la base de données NerdDinner dans le Explorer de serveur, puis sélectionner les tables que nous voulons modéliser dans celle-ci :

Capture d’écran de server Explorer. Les tables sont développées. Dîners et RS V P sont mis en évidence.

Nous pouvons ensuite faire glisser les tables sur l’aire du concepteur LINQ to SQL. Lorsque nous procédons ainsi, LINQ to SQL crée automatiquement des classes Dinner et RSVP à l’aide du schéma des tables (avec des propriétés de classe qui correspondent aux colonnes de la table de base de données) :

Capture d’écran de la boîte de dialogue Nerd Dinner. Les cours Dinner et R S VP sont affichés.

Par défaut, le concepteur LINQ to SQL « pluralise » automatiquement les noms de tables et de colonnes lorsqu’il crée des classes basées sur un schéma de base de données. Par exemple : la table « Dinners » dans notre exemple ci-dessus a donné lieu à un cours « Dinner ». Ce nommage de classe permet de rendre nos modèles cohérents avec les conventions d’affectation de noms .NET, et je trouve généralement que le concepteur résout ce problème de manière pratique (en particulier lors de l’ajout d’un grand nombre de tables). Si vous n’aimez pas le nom d’une classe ou d’une propriété générée par le concepteur, vous pouvez toujours le remplacer et le remplacer par le nom de votre choix. Pour ce faire, vous pouvez modifier le nom de l’entité/de la propriété en ligne dans le concepteur ou en le modifiant via la grille des propriétés.

Par défaut, le concepteur de LINQ to SQL inspecte également les relations clé primaire/clé étrangère des tables et, en fonction de celles-ci, crée automatiquement des « associations de relations » par défaut entre les différentes classes de modèle qu’il crée. Par exemple, lorsque nous avons fait glisser les tables Dinners et RSVP sur le LINQ to SQL concepteur, une association de relation un-à-plusieurs entre les deux a été déduite en fonction du fait que la table RSVP avait une clé étrangère à la table Dinners (ce qui est indiqué par la flèche dans le concepteur) :

Capture d’écran des tables Dinner et RS VP. Une flèche est mise en surbrillance et pointant à partir de l’arborescence des propriétés du dîner et de l’arborescence des propriétés RS VP.

L’association ci-dessus amène LINQ to SQL à ajouter une propriété « Dinner » fortement typée à la classe RSVP que les développeurs peuvent utiliser pour accéder au dîner associé à un RSVP donné. La classe Dinner aura également une propriété de collection « RSVPs » qui permet aux développeurs de récupérer et de mettre à jour des objets RSVP associés à un Dinner particulier.

Vous trouverez ci-dessous un exemple d’intellisense dans Visual Studio lorsque nous créons un objet RSVP et l’ajoutons à une collection RSVPs de Dinner. Notez comment LINQ to SQL ajouté automatiquement une collection « RSVPs » sur l’objet Dinner :

Capture d’écran d’intellisense dans Visual Studio. R S V Ps est mis en surbrillance.

En ajoutant l’objet RSVP à la collection RSVP de Dinner, nous disons LINQ to SQL d’associer une relation de clé étrangère entre la ligne Dinner et la ligne RSVP dans notre base de données :

Capture d’écran de l’objet R S VP et de la collection R S V P de Dinner.

Si vous n’aimez pas la façon dont le concepteur a modélisé ou nommé une association de table, vous pouvez la remplacer. Cliquez simplement sur la flèche d’association dans le concepteur et accédez à ses propriétés via la grille de propriétés pour la renommer, la supprimer ou la modifier. Toutefois, pour notre application NerdDinner, les règles d’association par défaut fonctionnent bien pour les classes de modèle de données que nous créons et nous pouvons simplement utiliser le comportement par défaut.

NerdDinnerDataContext, classe

Visual Studio crée automatiquement des classes .NET qui représentent les modèles et les relations de base de données définis à l’aide du concepteur LINQ to SQL. Une classe DataContext LINQ to SQL est également générée pour chaque fichier de concepteur LINQ to SQL ajouté à la solution. Étant donné que nous avons nommé notre élément de classe LINQ to SQL « NerdDinner », la classe DataContext créée sera appelée « NerdDinnerDataContext ». Cette classe NerdDinnerDataContext est le principal moyen d’interagir avec la base de données.

Notre classe NerdDinnerDataContext expose deux propriétés : « Dinners » et « RSVPs » qui représentent les deux tables que nous avons modélisées dans la base de données. Nous pouvons utiliser C# pour écrire des requêtes LINQ sur ces propriétés afin d’interroger et de récupérer des objets Dinner et RSVP de la base de données.

Le code suivant montre comment instancier un objet NerdDinnerDataContext et exécuter une requête LINQ sur celui-ci pour obtenir une séquence de dîners qui se produisent à l’avenir. Visual Studio fournit une intellisense complète lors de l’écriture de la requête LINQ, et les objets retournés à partir de celle-ci sont fortement typés et prennent également en charge intellisense :

Capture d’écran de Visual Studio. La description est mise en surbrillance.

En plus de nous permettre d’interroger les objets Dinner et RSVP, un NerdDinnerDataContext suit automatiquement toutes les modifications que nous apportons par la suite aux objets Dinner et RSVP que nous récupérons par son intermédiaire. Nous pouvons utiliser cette fonctionnalité pour enregistrer facilement les modifications dans la base de données, sans avoir à écrire de code de mise à jour SQL explicite.

Par exemple, le code ci-dessous montre comment utiliser une requête LINQ pour récupérer un seul objet Dinner de la base de données, mettre à jour deux des propriétés Dinner, puis enregistrer les modifications dans la base de données :

NerdDinnerDataContext db = new NerdDinnerDataContext();

// Retrieve Dinner object that reprents row with DinnerID of 1
Dinner dinner = db.Dinners.Single(d => d.DinnerID == 1);

// Update two properties on Dinner 
dinner.Title = "Changed Title";
dinner.Description = "This dinner will be fun";

// Persist changes to database
db.SubmitChanges();

L’objet NerdDinnerDataContext dans le code ci-dessus a automatiquement suivi les modifications de propriété apportées à l’objet Dinner que nous avons récupéré à partir de celui-ci. Lorsque nous avons appelé la méthode « SubmitChanges() », elle exécute une instruction SQL « UPDATE » appropriée dans la base de données pour conserver les valeurs mises à jour.

Création d’une classe DinnerRepository

Pour les petites applications, il est parfois possible que les contrôleurs fonctionnent directement sur une classe DataContext LINQ to SQL et incorporent des requêtes LINQ dans les contrôleurs. Cependant, à mesure que les applications deviennent plus volumineuses, cette approche devient fastidieuse à gérer et à tester. Cela peut également nous conduire à dupliquer les mêmes requêtes LINQ à plusieurs endroits.

Une approche qui peut rendre les applications plus faciles à gérer et à tester consiste à utiliser un modèle de « dépôt ». Une classe de référentiel permet d’encapsuler la logique d’interrogation et de persistance des données, et extrait les détails d’implémentation de la persistance des données de l’application. En plus de rendre le code d’application plus propre, l’utilisation d’un modèle de référentiel peut faciliter la modification des implémentations de stockage de données à l’avenir et faciliter le test unitaire d’une application sans nécessiter de base de données réelle.

Pour notre application NerdDinner, nous allons définir une classe DinnerRepository avec la signature ci-dessous :

public class DinnerRepository {

    // Query Methods
    public IQueryable<Dinner> FindAllDinners();
    public IQueryable<Dinner> FindUpcomingDinners();
    public Dinner             GetDinner(int id);

    // Insert/Delete
    public void Add(Dinner dinner);
    public void Delete(Dinner dinner);

    // Persistence
    public void Save();
}

Remarque : Plus loin dans ce chapitre, nous allons extraire une interface IDinnerRepository de cette classe et activer l’injection de dépendances avec celle-ci sur nos contrôleurs. Pour commencer, cependant, nous allons commencer simple et simplement travailler directement avec la classe DinnerRepository.

Pour implémenter cette classe, nous allons cliquer avec le bouton droit sur notre dossier « Modèles », puis choisir la commande de menu Ajouter un> nouvel élément . Dans la boîte de dialogue « Ajouter un nouvel élément », nous allons sélectionner le modèle « Classe » et nommer le fichier « DinnerRepository.cs » :

Capture d’écran du dossier Models. Ajouter un nouvel élément est mis en surbrillance.

Nous pouvons ensuite implémenter notre classe DinnerRepository à l’aide du code ci-dessous :

public class DinnerRepository {
 
    private NerdDinnerDataContext db = new NerdDinnerDataContext();

    //
    // Query Methods

    public IQueryable<Dinner> FindAllDinners() {
        return db.Dinners;
    }

    public IQueryable<Dinner> FindUpcomingDinners() {
        return from dinner in db.Dinners
               where dinner.EventDate > DateTime.Now
               orderby dinner.EventDate
               select dinner;
    }

    public Dinner GetDinner(int id) {
        return db.Dinners.SingleOrDefault(d => d.DinnerID == id);
    }

    //
    // Insert/Delete Methods

    public void Add(Dinner dinner) {
        db.Dinners.InsertOnSubmit(dinner);
    }

    public void Delete(Dinner dinner) {
        db.RSVPs.DeleteAllOnSubmit(dinner.RSVPs);
        db.Dinners.DeleteOnSubmit(dinner);
    }

    //
    // Persistence 

    public void Save() {
        db.SubmitChanges();
    }
}

Récupération, mise à jour, insertion et suppression à l’aide de la classe DinnerRepository

Maintenant que nous avons créé notre classe DinnerRepository, examinons quelques exemples de code qui illustrent les tâches courantes que nous pouvons effectuer avec elle :

Exemples d’interrogation

Le code ci-dessous récupère un seul Dinner à l’aide de la valeur DinnerID :

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

Le code ci-dessous récupère tous les dîners à venir et les boucle :

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve all upcoming Dinners
var upcomingDinners = dinnerRepository.FindUpcomingDinners();

// Loop over each upcoming Dinner and print out its Title
foreach (Dinner dinner in upcomingDinners) {
   Response.Write("Title" + dinner.Title);
}

Exemples d’insertion et de mise à jour

Le code ci-dessous illustre l’ajout de deux nouveaux dîners. Les ajouts/modifications apportés au dépôt ne sont pas validées dans la base de données tant que la méthode « Save() » n’est pas appelée. LINQ to SQL encapsule automatiquement toutes les modifications d’une transaction de base de données . Ainsi, toutes les modifications se produisent ou aucune d’entre elles ne le font lorsque notre dépôt enregistre :

DinnerRepository dinnerRepository = new DinnerRepository();

// Create First Dinner
Dinner newDinner1 = new Dinner();
newDinner1.Title = "Dinner with Scott";
newDinner1.HostedBy = "ScotGu";
newDinner1.ContactPhone = "425-703-8072";

// Create Second Dinner
Dinner newDinner2 = new Dinner();
newDinner2.Title = "Dinner with Bill";
newDinner2.HostedBy = "BillG";
newDinner2.ContactPhone = "425-555-5151";

// Add Dinners to Repository
dinnerRepository.Add(newDinner1);
dinnerRepository.Add(newDinner2);

// Persist Changes
dinnerRepository.Save();

Le code ci-dessous récupère un objet Dinner existant et modifie deux propriétés sur celui-ci. Les modifications sont validées dans la base de données lorsque la méthode « Save() » est appelée sur notre dépôt :

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Update Dinner properties
dinner.Title = "Update Title";
dinner.HostedBy = "New Owner";

// Persist changes
dinnerRepository.Save();

Le code ci-dessous récupère un dîner, puis y ajoute un RSVP. Pour ce faire, il utilise la collection RSVPs sur l’objet Dinner que LINQ to SQL créé pour nous (car il existe une relation clé primaire/clé étrangère entre les deux dans la base de données). Cette modification est conservée dans la base de données en tant que nouvelle ligne de table RSVP lorsque la méthode « Save() » est appelée sur le dépôt :

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Create a new RSVP object
RSVP myRSVP = new RSVP();
myRSVP.AttendeeName = "ScottGu";

// Add RSVP to Dinner's RSVP Collection
dinner.RSVPs.Add(myRSVP);

// Persist changes
dinnerRepository.Save();

Exemple de suppression

Le code ci-dessous récupère un objet Dinner existant, puis le marque à supprimer. Lorsque la méthode « Save() » est appelée sur le dépôt, elle valide la suppression dans la base de données :

DinnerRepository dinnerRepository = new DinnerRepository();

// Retrieve specific dinner by its DinnerID
Dinner dinner = dinnerRepository.GetDinner(5);

// Mark dinner to be deleted
dinnerRepository.Delete(dinner);

// Persist changes
dinnerRepository.Save();

Intégration de la logique de validation et de règle métier à des classes de modèle

L’intégration de la logique de validation et de règle métier est un élément clé de toute application qui fonctionne avec des données.

Validation de schéma

Lorsque des classes de modèle sont définies à l’aide du concepteur de LINQ to SQL, les types de données des propriétés des classes de modèle de données correspondent aux types de données de la table de base de données. Par exemple : si la colonne « EventDate » dans la table Dinners est « datetime », la classe de modèle de données créée par LINQ to SQL sera de type « DateTime » (qui est un type de données .NET intégré). Cela signifie que vous obtiendrez des erreurs de compilation si vous tentez de lui attribuer un entier ou booléen à partir du code, et qu’une erreur s’affiche automatiquement si vous tentez de convertir implicitement un type de chaîne non valide au moment de l’exécution.

LINQ to SQL gère également automatiquement les valeurs SQL qui s’échappent pour vous lors de l’utilisation de chaînes, ce qui vous protège contre les attaques par injection SQL lors de leur utilisation.

Validation et logique de règle métier

La validation de schéma est utile dans un premier temps, mais elle est rarement suffisante. La plupart des scénarios réels nécessitent la possibilité de spécifier une logique de validation plus riche qui peut s’étendre sur plusieurs propriétés, exécuter du code et souvent connaître l’état d’un modèle (par exemple, s’il est créé /mis à jour/supprimé, ou dans un état spécifique à un domaine comme « archivé »). Il existe différents modèles et infrastructures qui peuvent être utilisés pour définir et appliquer des règles de validation aux classes de modèle, et il existe plusieurs frameworks .NET qui peuvent être utilisés pour y remédier. Vous pouvez utiliser pratiquement n’importe laquelle d’entre elles dans ASP.NET applications MVC.

Pour les besoins de notre application NerdDinner, nous allons utiliser un modèle relativement simple et direct où nous exposons une propriété IsValid et une méthode GetRuleViolations() sur notre objet de modèle Dinner. La propriété IsValid retourne true ou false selon que les règles de validation et d’entreprise sont toutes valides. La méthode GetRuleViolations() retourne une liste des erreurs de règle.

Nous allons implémenter IsValid et GetRuleViolations() pour notre modèle Dinner en ajoutant une « classe partielle » à notre projet. Les classes partielles peuvent être utilisées pour ajouter des méthodes/propriétés/événements aux classes gérées par un concepteur VS (comme la classe Dinner générée par le concepteur de LINQ to SQL) et éviter que l’outil ne gâche notre code. Nous pouvons ajouter une nouvelle classe partielle à notre projet en cliquant avec le bouton droit sur le dossier \Models, puis en sélectionnant la commande de menu « Ajouter un nouvel élément ». Nous pouvons ensuite choisir le modèle « Classe » dans la boîte de dialogue « Ajouter un nouvel élément » et le nommer Dinner.cs.

Capture d’écran du dossier Modèles. Ajouter un nouvel élément est sélectionné. Dinner dot c s est écrit dans la boîte de dialogue Ajouter un nouvel élément.

Cliquez sur le bouton « Ajouter » pour ajouter un fichier Dinner.cs à notre projet et l’ouvrir dans l’IDE. Nous pouvons ensuite implémenter une infrastructure d’application de règles/validation de base à l’aide du code ci-dessous :

public partial class Dinner {

    public bool IsValid {
        get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> GetRuleViolations() {
        yield break;
    }

    partial void OnValidate(ChangeAction action) {
        if (!IsValid)
            throw new ApplicationException("Rule violations prevent saving");
    }
}

public class RuleViolation {

    public string ErrorMessage { get; private set; }
    public string PropertyName { get; private set; }

    public RuleViolation(string errorMessage, string propertyName) {
        ErrorMessage = errorMessage;
        PropertyName = propertyName;
    }
}

Quelques remarques sur le code ci-dessus :

  • La classe Dinner est précédée d’une mot clé « partielle », ce qui signifie que le code qu’elle contient sera combiné avec la classe générée/gérée par le concepteur de LINQ to SQL et compilée dans une seule classe.
  • La classe RuleViolation est une classe d’assistance que nous allons ajouter au projet qui nous permet de fournir plus de détails sur une violation de règle.
  • La méthode Dinner.GetRuleViolations() permet d’évaluer nos règles de validation et d’entreprise (nous les implémenterons prochainement). Il retourne ensuite une séquence d’objets RuleViolation qui fournissent plus de détails sur les erreurs de règle.
  • La propriété Dinner.IsValid fournit une propriété d’assistance pratique qui indique si l’objet Dinner a des RuleViolations actives. Il peut être vérifié de manière proactive par un développeur à l’aide de l’objet Dinner à tout moment (et ne soulève pas d’exception).
  • La méthode partielle Dinner.OnValidate() est un crochet fourni LINQ to SQL qui nous permet d’être avertis chaque fois que l’objet Dinner est sur le point d’être conservé dans la base de données. Notre implémentation OnValidate() ci-dessus garantit que le Dîner n’a pas de RuleViolations avant d’être enregistré. S’il est dans un état non valide, une exception est levée, ce qui entraîne l’abandon de la transaction par LINQ to SQL.

Cette approche fournit une infrastructure simple dans laquelle nous pouvons intégrer des règles de validation et d’entreprise. Pour l’instant, ajoutons les règles ci-dessous à notre méthode GetRuleViolations() :

public IEnumerable<RuleViolation> GetRuleViolations() {

    if (String.IsNullOrEmpty(Title))
        yield return new RuleViolation("Title required","Title");

    if (String.IsNullOrEmpty(Description))
        yield return new RuleViolation("Description required","Description");

    if (String.IsNullOrEmpty(HostedBy))
        yield return new RuleViolation("HostedBy required", "HostedBy");

    if (String.IsNullOrEmpty(Address))
        yield return new RuleViolation("Address required", "Address");

    if (String.IsNullOrEmpty(Country))
        yield return new RuleViolation("Country required", "Country");

    if (String.IsNullOrEmpty(ContactPhone))
        yield return new RuleViolation("Phone# required", "ContactPhone");

    if (!PhoneValidator.IsValidNumber(ContactPhone, Country))
        yield return new RuleViolation("Phone# does not match country", "ContactPhone");

    yield break;
}

Nous utilisons la fonctionnalité « retour de rendement » de C# pour renvoyer une séquence de règlesViolations. Les six premières vérifications de règle ci-dessus appliquent simplement que les propriétés de chaîne sur notre Dîner ne peuvent pas être null ou vides. La dernière règle est un peu plus intéressante et appelle une méthode d’assistance PhoneValidator.IsValidNumber() que nous pouvons ajouter à notre projet pour vérifier que le format de numéro de téléphone contact correspond au pays/région du dîner.

Nous pouvons utiliser . Prise en charge des expressions régulières de NET pour implémenter cette prise en charge de la validation téléphonique. Vous trouverez ci-dessous une implémentation simple de PhoneValidator que nous pouvons ajouter à notre projet qui nous permet d’ajouter des vérifications de modèle Regex spécifiques à un pays/région :

public class PhoneValidator {

    static IDictionary<string, Regex> countryRegex = new Dictionary<string, Regex>() {
           { "USA", new Regex("^[2-9]\\d{2}-\\d{3}-\\d{4}$")},
           { "UK", new Regex("(^1300\\d{6}$)|(^1800|1900|1902\\d{6}$)|(^0[2|3|7|8]{1}[0-9]{8}$)|(^13\\d{4}$)|(^04\\d{2,3}\\d{6}$)")},
           { "Netherlands", new Regex("(^\\+[0-9]{2}|^\\+[0-9]{2}\\(0\\)|^\\(\\+[0-9]{2}\\)\\(0\\)|^00[0-9]{2}|^0)([0-9]{9}$|[0-9\\-\\s]{10}$)")},
    };

    public static bool IsValidNumber(string phoneNumber, string country) {

        if (country != null && countryRegex.ContainsKey(country))
            return countryRegex[country].IsMatch(phoneNumber);
        else
            return false;
    }

    public static IEnumerable<string> Countries {
        get {
            return countryRegex.Keys;
        }
    }
}

Gestion de la validation et des violations de logique métier

Maintenant que nous avons ajouté le code de validation et de règle d’entreprise ci-dessus, chaque fois que nous essayons de créer ou de mettre à jour un Dîner, nos règles logiques de validation sont évaluées et appliquées.

Les développeurs peuvent écrire du code comme ci-dessous pour déterminer de manière proactive si un objet Dinner est valide et récupérer une liste de toutes les violations qu’il contient sans lever d’exceptions :

Dinner dinner = dinnerRepository.GetDinner(5);

dinner.Country = "USA";
dinner.ContactPhone = "425-555-BOGUS";

if (!dinner.IsValid) {

    var errors = dinner.GetRuleViolations();
    
    // do something to fix the errors
}

Si nous essayons d’enregistrer un dîner dans un état non valide, une exception est levée lorsque nous appelons la méthode Save() sur le DinnerRepository. Cela se produit parce que LINQ to SQL appelle automatiquement notre méthode partielle Dinner.OnValidate() avant d’enregistrer les modifications de Dinner, et nous avons ajouté du code à Dinner.OnValidate() pour déclencher une exception si des violations de règles existent dans le Dinner. Nous pouvons intercepter cette exception et récupérer de manière réactive une liste des violations à corriger :

Dinner dinner = dinnerRepository.GetDinner(5);

try {

    dinner.Country = "USA";
    dinner.ContactPhone = "425-555-BOGUS";

    dinnerRepository.Save();
}
catch {

    var errors = dinner.GetRuleViolations();

    // do something to fix errors
}

Étant donné que nos règles de validation et d’entreprise sont implémentées dans notre couche de modèle, et non dans la couche d’interface utilisateur, elles seront appliquées et utilisées dans tous les scénarios au sein de notre application. Nous pourrons par la suite modifier ou ajouter des règles d’entreprise et que tout le code qui fonctionne avec nos objets Dinner les honore.

La possibilité de modifier les règles métier au même endroit, sans que ces modifications se répercutent sur l’ensemble de l’application et de la logique de l’interface utilisateur, est un signe d’une application bien écrite et un avantage qu’une infrastructure MVC aide à encourager.

étape suivante

Nous avons maintenant un modèle que nous pouvons utiliser pour interroger et mettre à jour notre base de données.

Nous allons maintenant ajouter des contrôleurs et des vues au projet que nous pouvons utiliser pour créer une expérience d’interface utilisateur HTML autour de celui-ci.