• 30 hours
  • Medium

Free online content available in this course.

course.header.alt.is_certifying

You can get support and mentoring from a private teacher via videoconference on this course.

Got it!

Last updated on 11/23/17

Le modèle

Log in or subscribe for free to enjoy all this course has to offer!

Nous avons déjà parlé un peu de modèle dans l’étude théorique de MVC, puis dans notre Hello World rapide. Je vous rappelle donc que le modèle correspond au M de MVC. Il s’agit des données et de l’état de votre application web, en général un ensemble de classes permettant d’accéder à vos données.

Nous avons créé dans le Hello World un modèle simpliste qui possède une classe Client  composée de deux propriétés simples permettant de stocker son nom et son âge. En général, les classes contenant le modèle sont des classes POCO.

Typiquement, avec une classe POCO Client, nous pourrons mettre son nom, son âge, mais aussi son prénom, pourquoi pas sa taille, sa date de naissance, etc. Par contre, cette classe ne contient pas de méthodes. Un objet Client  sera rempli par quelque chose et nous pourrons par exemple obtenir une liste de clients. C’est ce que nous avons fait dans le Hello World, j’avais créé une classe Clients  qui contenait une méthode ObtenirListeClients  et qui nous renvoyait une telle liste avec des données en dur.

En général les données ne sont pas directement écrites dans les fichiers C# car elles peuvent évoluer dynamiquement. Il est évident que je ne vais pas modifier mon application web à chaque fois que j’ai un nouveau client…
Bien souvent, nos données seront stockées en base de données. Ce n’est pas une obligation mais la base de données est un système très évolué dont le but est justement de stocker efficacement des données.

Présentation de l’application web fil rouge

Alors, le blabla c’est bien, mais quand c’est concret on comprend mieux ! Déjà, quand ça parle de client, on voit tout de suite mieux de quoi il s’agit.
Pour que cela soit un maximum concret, nous allons réaliser une petite application jusqu'à la fin de ce cours et cette application servira de fil rouge. Au fur et à mesure de nos apprentissages, nous pourrons améliorer cette application avec toutes les bonnes choses que nous allons découvrir.
Cette application est plutôt utile car je l’ai créée pour un besoin précis, à savoir m’aider, moi et mes collègues de travail, à choisir dans quel restaurant nous allions manger les vendredis midis.

C’est souvent une tradition entre collègues, on se fait une petite bouffe le vendredi midi pour fêter la fin de semaine. Mais c’est toujours un calvaire… Il y a plein de restaurants près du travail, et chacun a des envies différentes… Plutôt celui-ci, et à la rigueur celui-là,… Bref, plutôt que de longs débats qui nous obligeraient à faire durer la pause-café, une petite application MVC !!

Le principe est simple. Il y a une liste de restaurants disponibles, sachant que cette liste peut être enrichie par tout le monde. Chaque personne peut voter pour un ou plusieurs restaurants.

Bien sûr, le restaurant choisi est celui qui récoltera le plus de vote. Le vote est aveugle, c’est-à-dire qu’on ne connait pas les votes des autres tant qu’on n’a pas voté. Une fois voté, il est impossible de changer son choix.
(Pour la petite histoire, au début j’avais offert la possibilité de changer son vote dynamiquement, mais des alliances se formaient discrètement au fur et à mesure en fonction des différents votes et au final, on finissait toujours au même endroit. :-°)

Pour que l’application fonctionne de semaine en semaine, chaque vendredi aura sa propre session, session créée par le premier utilisateur souhaitant initier le sondage. L’utilisateur ayant créé la session fournit aux autres le lien unique du sondage en cours.
Et tout ceci sera bien sûr persisté en base de données, pour que tout le monde en profite. ^^

En réalité, il s’agit ici d’une version simplifiée de l’application que j’ai vraiment réalisée. Dans celle-ci, il y avait un système de pondération par vote. Un unique vote apportait plus de points à un restaurant qu’un vote multiple. De même, on pouvait indiquer qu’on ne venait pas ou simplement qu’on était indécis et qu’on suivait le résultat. Bref, des améliorations que chacun saura réaliser en fonction de ses propres besoins. :)

Entity Framework pour le modèle

Le modèle c’est donc toutes les classes qui vont nous permettre de « modéliser » notre application, ainsi que la couche permettant de faire persister les informations.
Il y a plusieurs solutions pour faire persister des données, mais je vais présenter ici comment nous pouvons enregistrer ces données en base de données. C’est le cas le plus classique, le plus performant et celui que vous rencontrerez très souvent en entreprise.
Pour cet accès aux données, nous allons utiliser une base de données SQL Server locale, ce qui nous évitera plein de configurations de serveurs dont nous ne voulons pas nous occuper dans ce tutoriel.

Il y a plusieurs solutions pour accéder à une base de données, ici nous allons par exemple utiliser un ORM comme Entity Framework pour faciliter cet accès aux données.

Je ne vais pas décrire Entity Framework dans ses moindres détails. Si le sujet vous intéresse, n’hésitez pas à consulter d’autres tutoriels. Ici, nous allons simplement nous servir d’Entity Framework en comprenant grosso modo ce que nous faisons, mais en laissant de côté la partie maîtrise avancée de l’outil.

Modéliser ses données et créer une base

Nous allons commencer par créer un nouveau projet pour notre application fil rouge, une application MVC 4 bien sûr ! Nous pouvons l’appeler ChoixResto par exemple, ou n’importe quel nom qui vous fait plaisir ; ce sera bien sûr un projet « Empty ». N’hésitez pas à cocher la case permettant de créer un projet de test unitaire, nous en aurons besoin.

D’une manière générale, il y a plusieurs façons de créer sa couche d’accès aux données avec Entity Framework :

  • En générant un modèle à partir d’une base de données. Cela implique d’avoir déjà une base de données avec des tables et des relations. Très utile si vous intégrez une équipe travaillant sur une application existante. On l’appelle le Database First.

  • En créant un modèle à partir du concepteur de Visual Studio. Nous définissons les entités de notre modèle puis les relations entre elles, et Visual Studio nous génère automatiquement le script de base de données permettant de créer la structure de données en base correspondant au modèle. On l’appelle le Model First.

  • En codant directement les classes de nos entités. Visual Studio est, de la même façon, capable de générer la base de données correspondant à notre modèle objet. On l’appelle le Code First.

J’ai déjà décrit dans mon livre pour apprendre à développer en C# la technique du Model First, je vais ici vous présenter le Code First. Si vous avez déjà lu mon ouvrage, alors vous pourrez découvrir cette autre façon de faire. Ici, elle a aussi l’avantage d’être plus rapide à mettre en place et me réduire le nombre de copies d’écrans. D’une pierre, deux coups. :p

La première chose à faire est de référencer Entity Framework. Le plus simple est d’utiliser NuGet et d’aller chercher directement le package Entity Framework. Pour cela, faites un clic droit sur vos références et choisissez de gérer les packages NuGet :

Gestion des packages NuGet
Gestion des packages NuGet

Vous pourrez alors rechercher et installer le package grâce au mot clé EntityFramework :

Référencer Entity Framework
Référencer Entity Framework

Notre application doit gérer plusieurs objets, nous allons les créer dans le répertoire Models. Ajoutez dans un premier temps une classe Utilisateur . Chaque utilisateur doit posséder un identifiant technique et un prénom :

public class Utilisateur
{
    public int Id { get; set; }
    public string Prenom { get; set; }
}

Nous avons besoin de manipuler des restaurants. Créez la classe suivante où chaque restaurant possède également un identifiant technique, un nom et un numéro de téléphone :

public class Resto
{
    public int Id { get; set; }
    public string Nom { get; set; }
    public string Telephone { get; set; }
}

Un vote, c’est un utilisateur qui choisit un restaurant, nous aurons donc la classe suivante :

public class Vote
{
    public int Id { get; set; }
    public virtual Resto Resto { get; set; }
    public virtual Utilisateur Utilisateur { get; set; }
}

Et enfin, un sondage, c’est une liste de votes pour une date donnée :

public class Sondage
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public virtual List<Vote> Votes { get; set; }
}

Remarquez que certaines propriétés sont précédées du mot clé virtual , cela permet d’activer le chargement paresseux de la propriété par Entity Framework afin de permettre le chargement de ces objets à partir de leurs identifiants.

Nous avons désormais besoin d’un contexte permettant d’accéder à ces données et qui permet également que toute la magie d’Entity Framework opère. Il s’agit d’une nouvelle classe qui dérive de la classe DbContext , appelons là BddContext  :

public class BddContext : DbContext
{
    public DbSet<Sondage> Sondages { get; set; }
    public DbSet<Resto> Restos { get; set; }
}

Vous aurez besoin d’inclure l’espace de nom suivant :

using System.Data.Entity;

Le point d’entrée de ce contexte est la liste des sondages. Pour cela, nous utiliserons la classe DbSet  générique avec notre type Sondage . Nous avons également ajouté un autre point d’entrée, la liste des restaurants.
Et voilà, c’est terminé pour la première version de notre modèle (et oui, première version car nous (et surtout vous !) allons l’améliorer plus tard).

Accéder au modèle

Nous allons désormais pouvoir accéder au modèle et indirectement à la base de données. Pour cela, nous allons pouvoir utiliser notre point d’entrée BddContext .

En général, une bonne pratique consiste à encapsuler les différentes opérations que nous pouvons réaliser sur ce modèle. Il y a plusieurs patrons de conceptions qui décrivent ces bonnes pratiques, mais ils proposent globalement tous de réaliser une couche intermédiaire permettant d’abstraire le plus possible la couche d’accès aux données. Ce qui a pour but non seulement d’en faciliter l’accès, de créer un point d’entrée unique, et éventuellement de changer le type d’accès aux données sans perturber le reste du programme. Imaginons que nous souhaitions utiliser des services web à la place d’une base de données, ou que nous changions de base de données, ou encore que nous utilisions des fichiers… Bref, dans ce cas, notre couche d’abstraction supplémentaire gardera la même signature, seule l’implémentation changera.

Nous allons donc ici créer une couche d’accès aux données, souvent abréviée en DAL (pour Data Access Layer en anglais). Première chose à faire, définir le contrat de cette DAL. Qui dit contrat, dit interface. Nous allons donc indiquer les différentes méthodes dont nous allons avoir besoin. Par exemple, nous allons avoir besoin d’une méthode qui permet de récupérer la liste de tous les restaurants :

public interface IDal : IDisposable
{
    List<Resto> ObtientTousLesRestaurants();
}

Cette interface est bien sûr à placer dans le répertoire Models.

Remarquez que j’utilise ici directement dans mon interface la classe Resto qui est la classe du modèle que nous avons créé. C’est une pratique courante avec Entity Framework car ces objets sont de simples POCO que nous pouvons réutiliser à souhait.

Puis nous avons besoin de créer une classe qui implémente cette interface et qui va s’occuper de lister les différents restaurants enregistrés en base :

public class Dal : IDal
{
    private BddContext bdd;

    public Dal()
    {
        bdd = new BddContext();
    }

    public List<Resto> ObtientTousLesRestaurants()
    {
        return bdd.Restos.ToList();
    }

    public void Dispose()
    {
        bdd.Dispose();
    }
}

C’est plutôt simple ici. On utilise la classe BddContext  pour accéder aux différents éléments de la base de données. Nous voyons que nous instancions cet objet dans le constructeur de la DAL et que nous le supprimons proprement en appelant sa méthode Dispose . Le reste est tout simple, pour récupérer la liste de tous les restaurants, nous utilisons la propriété Restos  de cette classe.

Ajoutons à présent une méthode pour créer un nouveau restaurant, dans notre interface premièrement, puis son implémentation dans la DAL :

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
}

Pour créer un nouveau restaurant, il suffit de créer un objet Resto  et de l’ajouter à la liste des Restos :

public class Dal : IDal
{
    [...]

    public void CreerRestaurant(string nom, string telephone)
    {
        bdd.Restos.Add(new Resto { Nom = nom, Telephone = telephone });
        bdd.SaveChanges();
    }
}

On appellera la méthode SaveChanges  pour faire persister les modifications en base de données.

Tester le modèle

Alors, comment vérifier que tout cela fonctionne ?
En testant bien sûr !

Alors nous pourrions très bien créer une petite application MVC qui appellerait notre DAL… mais il y a une solution évidemment plus propre et plus pérenne. Vous avez deviné : les tests automatiques.
Maintenant que vous êtes des experts en tests, rien de plus simple.

Si ce n’est pas déjà fait, ajoutez un projet de test à votre solution ChoixResto , que nous appelons ChoixResto.Tests.
Et créons donc une nouvelle classe de tests : DalTests .

Nous allons développer un test qui permet de créer un restaurant et de vérifier qu’il existe bien en récupérant tous les restaurants. Facile :

[TestClass]
public class DalTests
{
    [TestMethod]
    public void CreerRestaurant_AvecUnNouveauRestaurant_ObtientTousLesRestaurantsRenvoitBienLeRestaurant()
    {
        using (IDal dal = new Dal())
        {
            dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
            List<Resto> restos = dal.ObtientTousLesRestaurants();
                
            Assert.IsNotNull(restos);
            Assert.AreEqual(1, restos.Count);
            Assert.AreEqual("La bonne fourchette", restos[0].Nom);
            Assert.AreEqual("01 02 03 04 05", restos[0].Telephone);
        }
    }
}

Il faudra également ajouter la référence à Entity Framework comme nous l'avons déjà fait pour l'application web, grâce à NuGet. Vous savez faire maintenant !

Puis exécutons le test… Et voilà, le test est vert, tout va bien :) :

Le test fonctionne
Le test fonctionne

Quoi ? Chez vous ça ne fonctionne pas ? Ça peut arriver malheureusement. :(
Si c’est le cas, vous avez sans doute le message d’erreur suivant :

La méthode de test […] a levé une exception :
System.Data.ProviderIncompatibleException: Une erreur s'est produite lors de la récupération d'informations du fournisseur depuis la base de données. Cela peut être causé par Entity Framework qui utilise une chaîne de connexion incorrecte. Vérifiez les exceptions internes pour plus d'informations et vérifiez que la chaîne de connexion est correcte. ---> System.Data.ProviderIncompatibleException: Le fournisseur n'a pas retourné de chaîne ProviderManifestToken. ---> System.Data.SqlClient.SqlException: Une erreur liée au réseau ou spécifique à l'instance s'est produite lors de l'établissement d'une connexion à SQL Server. Le serveur est introuvable ou n'est pas accessible. Vérifiez que le nom de l'instance est correct et que SQL Server est configuré pour autoriser les connexions distantes. (provider: SQL Network Interfaces, error: 26 - Erreur lors de la localisation du serveur/de l'instance spécifiés)

Pour résoudre ce problème, modifiez le fichier app.config de votre projet de test pour y mettre :

<connectionStrings>
  <add name="BddContext" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=ChoixResto.Models.BddContext;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFrameworkMUE" />
</connectionStrings>

Et oui, la chaîne de connexion. Pourquoi des fois ça marche sans et des fois avec ? Si vous le voulez bien, j’en parle juste après :D.

En tous cas, avec ça, votre test devrait maintenant passer.

Où sont mes données ?

Alors, OK, on parle d’une base de données depuis tout à l’heure, mais elle est où ?
En fait, c’est Entity Framework qui l’a créée pour nous. Par convention, lorsque nous utilisons l’approche Code First comme nous venons de le faire, Entity Framework se charge de créer la base de données (si non existante).
Il essaie dans un premier temps de se connecter à un Sql Server Express. S’il y arrive, alors il y crée la base. S’il n’y arrive pas, alors il essaie de se connecter à la base locale (LocalDb) pour y créer la base.

Comme nous avons installé Visual Studio 2013 (ou 2012), nous disposons par défaut de la base locale (LocalDb). Par contre, ceux qui possèdent Visual Studio 2010 ont normalement la possibilité d’installer Sql Server Express.

C’est en se connectant à la base locale que Visual Studio 2013 s'embrouille parfois. Si votre test a échoué la première fois, c’est parce qu’il a tenté de se connecter en premier lieu à Sql Server Express et qu’il n’a pas réussi. Nous lui indiquons avec cette chaîne de connexion où il doit se connecter, en l’occurrence sur localdb.

D’ailleurs, nous allons nous y connecter histoire de voir ce qui s’est passé dans cette fameuse base… Pour cela, allez dans l’explorateur de serveurs et cliquez sur Connexion à la base de données :

Connexion à la base de données locale
Connexion à la base de données locale

Si la source de données n'est pas positionnée sur Microsoft Sql Server (SqlClient) alors il va falloir cliquer sur modifier afin justement de choisir celle-là :

Modifier la source de données
Modifier la source de données

Puis sélectionnez « Microsoft Sql Server » :

Sql Server en source de données
Sql Server en source de données

De retour sur la fenêtre d’ajout de connexion, saisissez l’adresse du serveur ainsi que la base de données. L’adresse est (localdb)\v11.0 et le nom de la base sera ChoixResto.Models.BddContext :

Connexion sur le serveur local à la base de données
Connexion sur le serveur local à la base de données

Remarquez que la base de données s’appelle du même nom que notre classe de contexte, préfixée par le nom du projet.

Cliquez sur OK et vous pourrez voir notre base de données. Si vous dépliez l’arborescence et parcourez les tables, vous pourrez voir ceci :

Les tables de la base de données
Les tables de la base de données

Il s’agit de toutes les tables qui ont été créées par Entity Framework. Elles reflètent parfaitement notre modèle de données.

Une table Restoes qui représente nos restaurants avec un identifiant qui sert de clé primaire, un nom et un téléphone.

Ne faites pas attention à la table __MigrationHistory  qui est une table de migration utilisée par le code first, d'ailleurs il se peut que vous ne l'ayez même pas.

La table Utilisateurs  possède un identifiant et un prénom. La table des sondages possède un identifiant et une date. Mais où est passée notre liste de votes associées à notre sondage ? Elle se trouve dans la table Votes . Nous voyons que le vote possède un identifiant mais également l’identifiant du resto, de l’utilisateur et d’un sondage. C’est comme ça que se fait la relation.

Et nos identifiants ?

En fait, ils sont automatiques, c’est-à-dire auto-incrémentés et uniques. Nous pouvons le voir par exemple en sélectionnant le champ Id  de la table Utilisateurs . Nous avons les propriétés suivantes :

Structure du champ Id de la table Utilisateurs
Structure du champ Id de la table Utilisateurs

Nous pouvons voir que cet identifiant est « d'identité », cela veut dire qu’il est unique et auto-incrémenté. D’ailleurs l’incrément est de 1. Vous pouvez de la même façon voir le détail des autres propriétés si elles vous intéressent.

Comment cela se fait qu’Entity Framework ait décidé tout seul que notre propriété Id était la clé primaire, auto-incrémentée ? Et comment a-t-il décidé que les relations étaient faites sur l’identifiant des entités ?

Tout simplement grâce aux conventions d’Entity Framework. Par défaut, si vous nommez une propriété Id  (ou ID ) ou NomDeLaClasseId  alors Entity Framework arrive à comprendre tout seul que vous avez envie que cette propriété soit la clé primaire de votre entité.
C’est une convention qui permet de gagner du temps et de simplifier la tâche du développeur. Mais rassurez-vous, si malgré tout vous n’avez pas envie que votre identifiant s’appelle Id  ou que la clé primaire soit un identifiant non-numérique, il y a toujours moyen de le faire, nous verrons cela un peu plus loin.

Maintenant, faites un clic droit sur votre base de données et choisissez de faire une nouvelle requête :

Ouvrir une nouvelle requête
Ouvrir une nouvelle requête

Une nouvelle fenêtre apparaît qui va nous permettre de faire une requête SQL, qui est le langage pour interroger les bases de données. Je ne vous ferai pas une leçon sur le SQL car on sort carrément du cours, alors voici tout cuit de quoi voir ce qu’il y a dans la table des restaurants :

select * from Restoes

si vous appuyez sur le petit triangle pour exécuter la requête, vous pourrez voir son résultat :

Résultat de la requête SQL
Résultat de la requête SQL

à savoir le contenu de la table Restoes . Nous avons un identifiant auto-incrémenté : 1, le nom du restaurant et le numéro de téléphone

Et voilà où sont nos données… Parfait.

Réinitialiser la base de données

Quoique… Pas si parfait que ça en fait. Et si nous réexécutions notre test ? Notre test, c’est ce qui nous permet d’être certains que notre code fonctionne. Donc, il doit fonctionner vu que nous n’avons rien touché au code…

Vous voyez comme moi ? Je n’ai pas la berlue ?

Le test est en échec
Le test est en échec

Le test est rouge, diantre, fichtre !
Echec de Assert.AreEqual. Attendu 1 et en fait on a 2. Et c’est sur la ligne :

Assert.AreEqual(1, restos.Count);

Eh oui… en fait, notre test a pour but de créer un restaurant et de vérifier qu’il a bien été créé. Donc, la première fois qu’on a démarré notre test, il a ajouté le resto, le test est OK car il y a un seul resto. La deuxième fois, il y a deux restos, deux fois le même. Donc le test échoue.
Pour le vérifier, vous pouvez ré-exécuter la requête SQL et voir qu’il nous renvoie deux lignes, d’ailleurs nous pouvons voir au passage les id  qui s’auto-incrémentent.

Nous avons en fait un problème car les tests automatiques doivent être sans état afin d’être exécutables à l’infini. Or la base de données, c’est justement quelque chose qui a pour but de faire persister les états. Nous devons donc trouver une solution pour maîtriser l’état de départ de nos tests.
Il y a plusieurs solutions :

  • On pourrait faire en sorte que nos tests ne perturbent pas l’état de la base de données, cela veut dire par exemple qu’à chaque fois que l’on ajoute un élément, on le supprime ensuite. Cette technique est simple à mettre en œuvre mais pose un problème si jamais il y a une erreur à la suppression. On se retrouvera dans un état non maîtrisé.

  • On pourrait envisager de remettre la base à zéro à chaque fois que l’on exécute un test. Cela veut dire par exemple qu’on supprime la base avant chaque test, qu’on la recrée et que l’on insère (éventuellement) un jeu de données maîtrisé pour nos tests. Cela implique du développement supplémentaire et créer un jeu de données n’est pas toujours très simple si les données à tester sont complexes. Cela impose également de dédier une base de données aux tests et d’en avoir une autre pour les démos par exemples.

  • On peut également utiliser des framework qui exécutent les tests dans des transactions qui seront annulées après l’exécution de chaque test, comme ndbunit.

Nous allons utiliser ici la deuxième solution qui convient parfaitement à notre cas, car nous allons avoir besoin d’un jeu de données réduit, et nous sommes capables assez facilement d’utiliser plusieurs bases de données.
En effet, pour créer une nouvelle base de données, rien de plus simple. Il suffit de modifier (ou d’ajouter si vous avez eu la chance que votre test fonctionne du premier coup) la chaîne de connexion dans notre projet de test, située dans le fichier app.config. Utilisez cette nouvelle chaîne de connexion par exemple :

<connectionStrings>
  <add name="BddContext" providerName="System.Data.SqlClient" connectionString="Data Source=(localdb)\v11.0;Initial Catalog=ChoixResto.Models.BddContextTest;Integrated Security=True;MultipleActiveResultSets=True;Application Name=EntityFrameworkMUE" />
</connectionStrings>

Remarquez que la base de données s’appelle désormais BddContextTest. Exécutez à nouveau le test, celui-ci passe sans problème car une nouvelle base de données a été créée. Vous pouvez le vérifier en vous y connectant, comme nous l’avons fait précédemment.

La nouvelle base de tests
La nouvelle base de tests

Attention, dans la base de données de notre application web, ChoixResto.Models.BddContext, il y a désormais deux restaurants qui s’appellent pareil. Je vous encourage à aller supprimer le contenu de la table grâce à la requête suivante :

delete from Restoes

Bon, c’est très bien, mais nous nous retrouvons exactement avec le même problème, mais cette fois-ci c’est sur la base de tests. Il faut donc faire en sorte de réinitialiser la base de données. Pour cela, on utilise la commande magique suivante :

IDatabaseInitializer<BddContext> init = new DropCreateDatabaseAlways<BddContext>();
Database.SetInitializer(init);
init.InitializeDatabase(new BddContext());

Cette commande magique permet d’indiquer que nous souhaitons réinitialiser notre base de données en la supprimant (Drop) puis en la recréant à chaque fois (CreateDatabaseAlways).
Nous avons donc notre test qui sera :

[TestMethod]
public void CreerRestaurant_AvecUnNouveauRestaurant_ObtientTousLesRestaurantsRenvoitBienLeRestaurant()
{
    IDatabaseInitializer<BddContext> init = new DropCreateDatabaseAlways<BddContext>();
    Database.SetInitializer(init);
    init.InitializeDatabase(new BddContext());

    using (IDal dal = new Dal())
    {
        dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
        List<Resto> restos = dal.ObtientTousLesRestaurants();

        Assert.IsNotNull(restos);
        Assert.AreEqual(1, restos.Count);
        Assert.AreEqual("La bonne fourchette", restos[0].Nom);
        Assert.AreEqual("01 02 03 04 05", restos[0].Telephone);
    }
}

Et voilà, vous pouvez exécuter votre test à l’infini maintenant. Il passe à chaque fois. L’inconvénient est que la réinitialisation de la base prend du temps. Chez moi, le test prend entre 10 et 15 secondes à peu près, ce qui est quand même relativement long, surtout si on a beaucoup de tests… Mais bon, c’est le prix à payer avec cette méthode. Et puis, nous ne sommes pas à quelques minutes près pour nous assurer que notre application fonctionne .

Notez qu’il est possible que vous ayez une erreur si la base de données est déjà utilisée, du style :

La méthode de test […] a levé une exception :
System.Data.SqlClient.SqlException: Cannot drop database "ChoixResto.Models.BddContextTest" because it is currently in use.

Dans ce cas, n’hésitez pas à fermer les connexions que nous avions ouvertes via l’explorateur de base de données, voire à redémarrer Visual Studio et tout rentrera dans l’ordre.

Allez, une petite astuce avant de terminer cette section. Si nous créons un nouveau test (ce que vous ne manquerez pas de faire :p) pour tester d’autres éléments, alors il faut à nouveau appeler la méthode qui réinitialise la base de données en début de notre méthode de test.
Si toutes vos méthodes de test ont besoin d’appeler cette fameuse méthode, alors on peut utiliser le framework de test pour le faire à notre place, en décorant une méthode de l’attribut TestInitialize , ce qui donne :

[TestClass]
public class DalTests
{
    [TestInitialize]
    public void Init_AvantChaqueTest()
    {
        IDatabaseInitializer<BddContext> init = new DropCreateDatabaseAlways<BddContext>();
        Database.SetInitializer(init);
        init.InitializeDatabase(new BddContext());
    }

    [TestMethod]
    public void CreerRestaurant_AvecUnNouveauRestaurant_ObtientTousLesRestaurantsRenvoitBienLeRestaurant()
    {
        using (IDal dal = new Dal())
        {
            dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
            List<Resto> restos = dal.ObtientTousLesRestaurants();

            Assert.IsNotNull(restos);
            Assert.AreEqual(1, restos.Count);
            Assert.AreEqual("La bonne fourchette", restos[0].Nom);
            Assert.AreEqual("01 02 03 04 05", restos[0].Telephone);
        }
    }
}

La méthode décorée de cet attribut sera appelée avant chaque test, c’est l’endroit idéal pour réaliser notre réinitialisation de base de données.
À noter que l'équivalent pour avoir une méthode appelée après chaque test est l'attribut TestCleanup .

Les annotations

Vous vous rappelez de notre table Restoes  ?Ah là là, quel nom… Alors que toutes les autres tables ont été correctement nommées, ici Entity Framework nous a fait n’importe quoi… En fait, Entity Framework a essayé de pluraliser le nom de la table, sauf qu'il l'a fait en anglais. Est-ce que Resto  a une tête à être de l'anglais ? :waw:

Mais rassurez-vous, il est possible d’influer sur la génération automatique d’Entity Framework, afin qu’il utilise ce que nous voulons, plutôt que ses conventions.

Par exemple, pour le forcer à appeler ma table Restos, je peux décorer ma classe de l’attribut Table  :

[Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    public string Nom { get; set; }
    public string Telephone { get; set; }
}

Pour cela, on doit ajouter l’espace de nom :

using System.ComponentModel.DataAnnotations.Schema;

Et voilà, ma table porte le nom désiré :

La table Resto porte désormais le bon nom
La table Restos porte désormais le bon nom

On appelle ces attributs qui permettent d'influer sur le comportement de la génération de notre modèle les DataAnnotations. Il y en a beaucoup d’autres, nous n’allons pas pouvoir tous les voir ici.
Rappelez-vous par exemple des clés primaires. Il est possible de donner un autre nom ou un autre type à une clé primaire. Dans ce cas, il suffit de décorer la propriété de la classe de l’attribut [Key] , par exemple :

[Table("Restos")]
public class Resto
{
    [Key]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}

Mais ne le faites pas pour notre classe Resto . Je vous encourage ici à garder la clé primaire auto-incrémentée ; qui sait s’il n’y a pas deux restaurants qui portent le même nom ? Et puis, c’est une mauvaise pratique d’utiliser un nom comme clé primaire. Si jamais nous voulions modifier le nom du restaurant ? Nous serions obligés de supprimer l’enregistrement puis de le recréer, au lieu de simplement le mettre à jour.
Il existe également l’annotation [Required ], qui va permettre de rendre une propriété obligatoire. Par exemple sur un restaurant, il sera obligatoire d’avoir un nom, par contre on peut se passer d’un numéro de téléphone si jamais celui-ci n’accepte pas de réservation.
Voilà ce que ça donne :

[Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    [Required]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}

D’ailleurs, nous allons faire de même avec le prénom de l’utilisateur :

public class Utilisateur
{
    public int Id { get; set; }
    [Required]
    public string Prenom { get; set; }
}

En base de données, cette annotation mettra votre champ à non-null :

Avant et après l'utilisation de l'attribut Required
Avant et après l'utilisation de l'attribut Required

Notez également les attributs MaxLength  et MinLength  qui permettent de limiter la taille d’un champ. Par exemple, nous pouvons l’utiliser sur le prénom de l’utilisateur pour indiquer qu’il ne doit pas dépasser 80 caractères. Pourquoi 80 ? Ben, je ne sais pas, au pif ^^ :

public class Utilisateur
{
    public int Id { get; set; }
    [Required, MaxLength(80)]
    public string Prenom { get; set; }
}

Vous pouvez supprimer l'annotation MaxLength  sur le prénom, nous ne nous en servirons pas.

Remarquez que quand nous cumulons plusieurs annotations, nous pouvons les écrire sur une seule ligne.

Allez, je m’arrête là pour ces annotations, sachez qu’il en existe d’autres si vous souhaitez aller fouiller un peu sur Internet.

Vous avez pu voir, si vous avez ré exécuté les tests, qu’Entity Framework prend très bien en charge les modifications de modèle, ainsi que la modification de la structure de la base de données. Je n’en parlerai pas ici, mais si vous en avez besoin, n’hésitez pas à consulter des tutoriels sur ce sujet.

Modifier les données

Avant de terminer ce chapitre, je voulais quand même vous montrer comment modifier des données en base. Nous allons pour ce faire enrichir notre DAL pour permettre la modification d’un restaurant. Il est pertinent de modifier des éléments grâce à leurs identifiants. Pour cela, nous pouvons rajouter cette méthode dans notre interface :

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    void ModifierRestaurant(int id, string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
}

Pour l’implémentation, c’est en fait très facile. Il suffit dans un premier temps de rechercher le restaurant possédant cet identifiant, de le modifier et d’appliquer les changements :

public void ModifierRestaurant(int id, string nom, string telephone)
{
    Resto restoTrouve = bdd.Restos.FirstOrDefault(resto => resto.Id == id);
    if (restoTrouve != null)
    {
        restoTrouve.Nom = nom;
        restoTrouve.Telephone = telephone;
        bdd.SaveChanges();
    }
}

Reste à écrire un petit test pour vérifier que tout fonctionne :

[TestMethod]
public void ModifierRestaurant_CreationDUnNouveauRestaurantEtChangementNomEtTelephone_LaModificationEstCorrecteApresRechargement()
{
    using (IDal dal = new Dal())
    {
        dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
        List<Resto> restos = dal.ObtientTousLesRestaurants();
        int id = restos.First(r => r.Nom == "La bonne fourchette").Id;

        dal.ModifierRestaurant(id, "La bonne cuillère", null);

        restos = dal.ObtientTousLesRestaurants();
        Assert.IsNotNull(restos);
        Assert.AreEqual(1, restos.Count);
        Assert.AreEqual("La bonne cuillère", restos[0].Nom);
        Assert.IsNull(restos[0].Telephone);
    }
}

Et voilà. On commence par créer un restaurant, vu que notre base est vide ; puis on obtient l’identifiant de ce restaurant. Grâce à cet identifiant nous pouvons appeler la méthode de modification, puis vérifier que tout est OK.

Le code de la méthode de test devient donc :

using (IDal dal = new Dal())
{
    dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
    dal.ModifierRestaurant(1, "La bonne cuillère", null);

    List<Resto> restos = dal.ObtientTousLesRestaurants();
    Assert.IsNotNull(restos);
    Assert.AreEqual(1, restos.Count);
    Assert.AreEqual("La bonne cuillère", restos[0].Nom);
    Assert.IsNull(restos[0].Telephone);
}

En résumé

  • Le modèle consiste en des classes qui permettent d’accéder aux données de notre application.

  • Nous pouvons utiliser Entity Framework pour établir notre modèle et gérer sa persistance en base de données.

  • Avec Visual Studio 2012 et 2013, nous pouvons créer et utiliser facilement une base de données locale.

  • Il est possible d’agir sur la génération automatique de la base de données grâce à des annotations, qui prennent la forme d’attributs décorant le modèle.

  • Il est possible de maîtriser l’état des tests automatiques de base de données en réinitialisant la base au début de chaque test.

Example of certificate of achievement
Example of certificate of achievement