• 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

TP : Compléter le modèle

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

Nous avons commencé à implémenter notre modèle mais il n’est pas tout à fait fini. Et devinez quoi ? C’est vous qui allez le compléter à travers un petit TP qui va permettre de vérifier que vous avez bien tout compris du modèle.
Et puis comme ça, je serai sûr de ne pas être le seul à mettre les mains dans le cambouis. ;)

Instructions pour réaliser le TP

Bon, vous avez compris le principe. Je vais vous demander de compléter notre modèle et notre DAL. Voici ce que nous avons jusqu’à présent :

Côté Utilisateur  :

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

Côté Resto  :

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

Pour les Votes :

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

Et les Sondages :

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

La classe de contexte est :

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

Et pour l’instant, l’interface de la DAL est :

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

Puis son implémentation :

public class Dal : IDal
{
    private BddContext bdd;

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

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

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

    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();
        }
    }

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

Reste à améliorer ce modèle pour avoir un top modèle !

Une fois n’est pas coutume, pour la réalisation de ce TP, vous allez faire du TDD (Test Driven Development). Je vous ai écrit les tests que je souhaite voir passer avec plusieurs cas nominaux. Copiez-les dans votre solution et faites en sorte qu’ils passent tous. :) Voici la classe de test complète :

[TestClass]
public class DalTests
{
    private IDal dal;

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

        dal = new Dal();
    }

    [TestCleanup]
    public void ApresChaqueTest()
    {
        dal.Dispose();
    }

    [TestMethod]
    public void CreerRestaurant_AvecUnNouveauRestaurant_ObtientTousLesRestaurantsRenvoitBienLeRestaurant()
    {
        dal.CreerRestaurant("La bonne fourchette", "0102030405");
        List<Resto> restos = dal.ObtientTousLesRestaurants();

        Assert.IsNotNull(restos);
        Assert.AreEqual(1, restos.Count);
        Assert.AreEqual("La bonne fourchette", restos[0].Nom);
        Assert.AreEqual("0102030405", restos[0].Telephone);
    }

    [TestMethod]
    public void ModifierRestaurant_CreationDUnNouveauRestaurantEtChangementNomEtTelephone_LaModificationEstCorrecteApresRechargement()
    {
        dal.CreerRestaurant("La bonne fourchette", "0102030405");
        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);
    }

    [TestMethod]
    public void RestaurantExiste_AvecCreationDunRestauraunt_RenvoiQuilExiste()
    {
        dal.CreerRestaurant("La bonne fourchette", "0102030405");

        bool existe = dal.RestaurantExiste("La bonne fourchette");

        Assert.IsTrue(existe);
    }

    [TestMethod]
    public void RestaurantExiste_AvecRestaurauntInexistant_RenvoiQuilExiste()
    {
        bool existe = dal.RestaurantExiste("La bonne fourchette");

        Assert.IsFalse(existe);
    }

    [TestMethod]
    public void ObtenirUtilisateur_UtilisateurInexistant_RetourneNull()
    {
        Utilisateur utilisateur = dal.ObtenirUtilisateur(1);
        Assert.IsNull(utilisateur);
    }

    [TestMethod]
    public void ObtenirUtilisateur_IdNonNumerique_RetourneNull()
    {
        Utilisateur utilisateur = dal.ObtenirUtilisateur("abc");
        Assert.IsNull(utilisateur);
    }

    [TestMethod]
    public void AjouterUtilisateur_NouvelUtilisateurEtRecuperation_LutilisateurEstBienRecupere()
    {
        dal.AjouterUtilisateur("Nouvel utilisateur", "12345");

        Utilisateur utilisateur = dal.ObtenirUtilisateur(1);

        Assert.IsNotNull(utilisateur);
        Assert.AreEqual("Nouvel utilisateur", utilisateur.Prenom);

        utilisateur = dal.ObtenirUtilisateur("1");

        Assert.IsNotNull(utilisateur);
        Assert.AreEqual("Nouvel utilisateur", utilisateur.Prenom);
    }

    [TestMethod]
    public void Authentifier_LoginMdpOk_AuthentificationOK()
    {
        dal.AjouterUtilisateur("Nouvel utilisateur", "12345");

        Utilisateur utilisateur = dal.Authentifier("Nouvel utilisateur", "12345");

        Assert.IsNotNull(utilisateur);
        Assert.AreEqual("Nouvel utilisateur", utilisateur.Prenom);
    }

    [TestMethod]
    public void Authentifier_LoginOkMdpKo_AuthentificationKO()
    {
        dal.AjouterUtilisateur("Nouvel utilisateur", "12345");
        Utilisateur utilisateur = dal.Authentifier("Nouvel utilisateur", "0");

        Assert.IsNull(utilisateur);
    }

    [TestMethod]
    public void Authentifier_LoginKoMdpOk_AuthentificationKO()
    {
        dal.AjouterUtilisateur("Nouvel utilisateur", "12345");
        Utilisateur utilisateur = dal.Authentifier("Nouvel", "12345");

        Assert.IsNull(utilisateur);
    }

    [TestMethod]
    public void Authentifier_LoginMdpKo_AuthentificationKO()
    {
        Utilisateur utilisateur = dal.Authentifier("Nouvel utilisateur", "12345");

        Assert.IsNull(utilisateur);
    }

    [TestMethod]
    public void ADejaVote_AvecIdNonNumerique_RetourneFalse()
    {
        bool pasVote = dal.ADejaVote(1, "abc");

        Assert.IsFalse(pasVote);
    }

    [TestMethod]
    public void ADejaVote_UtilisateurNAPasVote_RetourneFalse()
    {
        int idSondage = dal.CreerUnSondage();
        int idUtilisateur = dal.AjouterUtilisateur("Nouvel utilisateur", "12345");

        bool pasVote = dal.ADejaVote(idSondage, idUtilisateur.ToString());

        Assert.IsFalse(pasVote);
    }

    [TestMethod]
    public void ADejaVote_UtilisateurAVote_RetourneTrue()
    {
        int idSondage = dal.CreerUnSondage();
        int idUtilisateur = dal.AjouterUtilisateur("Nouvel utilisateur", "12345");
        dal.CreerRestaurant("La bonne fourchette", "0102030405");
        dal.AjouterVote(idSondage, 1, idUtilisateur);

        bool aVote = dal.ADejaVote(idSondage, idUtilisateur.ToString());

        Assert.IsTrue(aVote);
    }

    [TestMethod]
    public void ObtenirLesResultats_AvecQuelquesChoix_RetourneBienLesResultats()
    {
        int idSondage = dal.CreerUnSondage();
        int idUtilisateur1 = dal.AjouterUtilisateur("Utilisateur1", "12345");
        int idUtilisateur2 = dal.AjouterUtilisateur("Utilisateur2", "12345");
        int idUtilisateur3 = dal.AjouterUtilisateur("Utilisateur3", "12345");

        dal.CreerRestaurant("Resto pinière", "0102030405");
        dal.CreerRestaurant("Resto pinambour", "0102030405");
        dal.CreerRestaurant("Resto mate", "0102030405");
        dal.CreerRestaurant("Resto ride", "0102030405");

        dal.AjouterVote(idSondage, 1, idUtilisateur1);
        dal.AjouterVote(idSondage, 3, idUtilisateur1);
        dal.AjouterVote(idSondage, 4, idUtilisateur1);
        dal.AjouterVote(idSondage, 1, idUtilisateur2);
        dal.AjouterVote(idSondage, 1, idUtilisateur3);
        dal.AjouterVote(idSondage, 3, idUtilisateur3);

        List<Resultats> resultats = dal.ObtenirLesResultats(idSondage);

        Assert.AreEqual(3, resultats[0].NombreDeVotes);
        Assert.AreEqual("Resto pinière", resultats[0].Nom);
        Assert.AreEqual("0102030405", resultats[0].Telephone);
        Assert.AreEqual(2, resultats[1].NombreDeVotes);
        Assert.AreEqual("Resto mate", resultats[1].Nom);
        Assert.AreEqual("0102030405", resultats[1].Telephone);
        Assert.AreEqual(1, resultats[2].NombreDeVotes);
        Assert.AreEqual("Resto ride", resultats[2].Nom);
        Assert.AreEqual("0102030405", resultats[2].Telephone);
    }

    [TestMethod]
    public void ObtenirLesResultats_AvecDeuxSondages_RetourneBienLesBonsResultats()
    {
        int idSondage1 = dal.CreerUnSondage();
        int idUtilisateur1 = dal.AjouterUtilisateur("Utilisateur1", "12345");
        int idUtilisateur2 = dal.AjouterUtilisateur("Utilisateur2", "12345");
        int idUtilisateur3 = dal.AjouterUtilisateur("Utilisateur3", "12345");
        dal.CreerRestaurant("Resto pinière", "0102030405");
        dal.CreerRestaurant("Resto pinambour", "0102030405");
        dal.CreerRestaurant("Resto mate", "0102030405");
        dal.CreerRestaurant("Resto ride", "0102030405");
        dal.AjouterVote(idSondage1, 1, idUtilisateur1);
        dal.AjouterVote(idSondage1, 3, idUtilisateur1);
        dal.AjouterVote(idSondage1, 4, idUtilisateur1);
        dal.AjouterVote(idSondage1, 1, idUtilisateur2);
        dal.AjouterVote(idSondage1, 1, idUtilisateur3);
        dal.AjouterVote(idSondage1, 3, idUtilisateur3);

        int idSondage2 = dal.CreerUnSondage();
        dal.AjouterVote(idSondage2, 2, idUtilisateur1);
        dal.AjouterVote(idSondage2, 3, idUtilisateur1);
        dal.AjouterVote(idSondage2, 1, idUtilisateur2);
        dal.AjouterVote(idSondage2, 4, idUtilisateur3);
        dal.AjouterVote(idSondage2, 3, idUtilisateur3);

        List<Resultats> resultats1 = dal.ObtenirLesResultats(idSondage1);
        List<Resultats> resultats2 = dal.ObtenirLesResultats(idSondage2);

        Assert.AreEqual(3, resultats1[0].NombreDeVotes);
        Assert.AreEqual("Resto pinière", resultats1[0].Nom);
        Assert.AreEqual("0102030405", resultats1[0].Telephone);
        Assert.AreEqual(2, resultats1[1].NombreDeVotes);
        Assert.AreEqual("Resto mate", resultats1[1].Nom);
        Assert.AreEqual("0102030405", resultats1[1].Telephone);
        Assert.AreEqual(1, resultats1[2].NombreDeVotes);
        Assert.AreEqual("Resto ride", resultats1[2].Nom);
        Assert.AreEqual("0102030405", resultats1[2].Telephone);

        Assert.AreEqual(1, resultats2[0].NombreDeVotes);
        Assert.AreEqual("Resto pinambour", resultats2[0].Nom);
        Assert.AreEqual("0102030405", resultats2[0].Telephone);
        Assert.AreEqual(2, resultats2[1].NombreDeVotes);
        Assert.AreEqual("Resto mate", resultats2[1].Nom);
        Assert.AreEqual("0102030405", resultats2[1].Telephone);
        Assert.AreEqual(1, resultats2[2].NombreDeVotes);
        Assert.AreEqual("Resto pinière", resultats2[2].Nom);
        Assert.AreEqual("0102030405", resultats2[2].Telephone);
    }
}

Bien sûr, après avoir copié-collé cette classe, votre projet ne compilera plus. La première chose à faire sera de rajouter les méthodes dans l’interface de la DAL, puis d’implémenter les méthodes. Il y aura aussi le modèle à compléter.

Vous vous sentez d’y aller sans instructions supplémentaires ? Alors n’hésitez pas et sautez la section suivante ! Sinon, vous pouvez aller y jeter un œil discret sans que personne ne vous regarde. ^^

Quelques précisions complémentaires

Une des premières choses que vous verrez, c’est que j’ai fait plusieurs modifications dans le modèle. J’ai par exemple rajouté un mot de passe dans la classe utilisateur, celui-ci devant être obligatoire.
J’ai aussi ajouté une classe de résultats. Même si vous pouvez deviner son contenu en regardant les tests, je vous la rajoute ici afin que vous l’ajoutiez dans le modèle :

public class Resultats
{
    public string Nom { get; set; }
    public string Telephone { get; set; }
    public int NombreDeVotes { get; set; }
}

Notez que cette classe est une classe de calcul et qu’elle ne doit pas être persistée en base de données. Donc… Elle ne doit pas apparaître dans la classe BddContext et surtout pas en tant que membre DbSet .
À propos, cette classe BddContext  doit être modifiée pour ajouter un accès direct aux utilisateurs.

Après, les méthodes parlent d’elles-mêmes. Vous allez devoir créer une méthode permettant d’ajouter un utilisateur :

int AjouterUtilisateur(string nom, string motDePasse);

puis une autre permettant de l’authentifier :

Utilisateur Authentifier(string nom, string motDePasse);

Vous n’allez évidemment pas stocker le mot de passe en clair et vous allez utiliser un système de salage MD5 pour enregistrer le mot de passe. Voici une méthode qui permet de réaliser un encodage en MD5 :

private string EncodeMD5(string motDePasse)
{
    string motDePasseSel = "ChoixResto" + motDePasse + "ASP.NET MVC";
    return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(ASCIIEncoding.Default.GetBytes(motDePasseSel)));
}

Attention, ce système de salage est imparfait et pourrait être plus complexe. Le sel que j’ai rajouté ne doit pas apparaître en clair dans le code. Il vaudrait mieux qu’il soit stocké par exemple dans le machine.config. Dans le cadre de notre TP, ce sera néanmoins suffisant.

Vous pourrez créer deux méthodes permettant d’obtenir un utilisateur à partir de son id, la version avec un entier numérique en paramètre et la version avec une chaîne en paramètre qu’il faudra convertir en entier :

Utilisateur ObtenirUtilisateur(int id);
Utilisateur ObtenirUtilisateur(string idStr);

Vous créerez également la méthode permettant d’ajouter un sondage et qui renverra son identifiant :

int CreerUnSondage();

On aura une méthode permettant de comptabiliser un vote, à partir de l’id du sondage, de l’id du resto et de l’id du restaurant :

void AjouterVote(int idSondage, int idResto, int idUtilisateur);

Puis la méthode renvoyant les résultats :

List<Resultats> ObtenirLesResultats(int idSondage);

Celle-ci va grouper les restaurants et indiquera le nombre de votes par restaurant.

Enfin, nous créerons une méthode utilitaire permettant de savoir si un utilisateur a déjà voté, ceci pour l’empêcher de frauder en votant plusieurs fois :

bool ADejaVote(int idSondage, string idStr);

C’est à vous de jouer maintenant. Vous avez tout ce qu’il vous faut pour nous faire une belle DAL qui va faire passer au vert tous ces tests, de quoi rendre jaloux le géant du même nom.
Bon courage. :)

Correction

Ça peut paraître long comme ça car j’ai été plutôt prolixe dans l’énoncé, mais en fait, le plus long a été d’écrire les tests, et ça, c’est moi qui l’ai fait. :p Alors si la longueur vous a découragé, allez tout de suite vous y remettre et faites-moi passer ces tests.
Sinon, pour les plus courageux, je suis sûr que vous avez réussi haut la main, non sans buter sur quelques petits points. Mais rien de trop insurmontable. Je suis fier de vous.

Quoi qu’il en soit, voici ma correction pour ce TP. Voici dans un premier temps les objets du modèle modifiés.

La classe Utilisateur  avec son mot de passe :

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

La classe de résultats que vous avez déjà vue, sauf si vous avez sauté le chapitre précédent :

public class Resultats
{
    public string Nom { get; set; }
    public string Telephone { get; set; }
    public int NombreDeVotes { get; set; }
}

La classe BddContext  avec sa propriété Utilisateurs :

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

Puis l’interface :

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    void ModifierRestaurant(int id, string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
    bool RestaurantExiste(string nom);
    int AjouterUtilisateur(string nom, string motDePasse);
    Utilisateur Authentifier(string nom, string motDePasse);
    Utilisateur ObtenirUtilisateur(int id);
    Utilisateur ObtenirUtilisateur(string idStr);
    int CreerUnSondage();
    void AjouterVote(int idSondage, int idResto, int idUtilisateur);
    List<Resultats> ObtenirLesResultats(int idSondage);
    bool ADejaVote(int idSondage, string idStr);
}

Et son implémentation :

public class Dal : IDal
{
    private BddContext bdd;

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

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

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

    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();
        }
    }

    public bool RestaurantExiste(string nom)
    {
        return bdd.Restos.Any(resto => string.Compare(resto.Nom, nom, StringComparison.CurrentCultureIgnoreCase) == 0);
    }

    public int AjouterUtilisateur(string nom, string motDePasse)
    {
        string motDePasseEncode = EncodeMD5(motDePasse);
        Utilisateur utilisateur = new Utilisateur { Prenom = nom, MotDePasse = motDePasseEncode };
        bdd.Utilisateurs.Add(utilisateur);
        bdd.SaveChanges();
        return utilisateur.Id;
    }

    public Utilisateur Authentifier(string nom, string motDePasse)
    {
        string motDePasseEncode = EncodeMD5(motDePasse);
        return bdd.Utilisateurs.FirstOrDefault(u => u.Prenom == nom && u.MotDePasse == motDePasseEncode);
    }

    public Utilisateur ObtenirUtilisateur(int id)
    {
        return bdd.Utilisateurs.FirstOrDefault(u => u.Id == id);
    }

    public Utilisateur ObtenirUtilisateur(string idStr)
    {
        int id;
        if (int.TryParse(idStr, out id))
            return ObtenirUtilisateur(id);
        return null;
    }

    public int CreerUnSondage()
    {
        Sondage sondage = new Sondage { Date = DateTime.Now };
        bdd.Sondages.Add(sondage);
        bdd.SaveChanges();
        return sondage.Id;
    }

    public void AjouterVote(int idSondage, int idResto, int idUtilisateur)
    {
        Vote vote = new Vote
        {
            Resto = bdd.Restos.First(r => r.Id == idResto),
            Utilisateur = bdd.Utilisateurs.First(u => u.Id == idUtilisateur)
        };
        Sondage sondage = bdd.Sondages.First(s => s.Id == idSondage);
        if (sondage.Votes == null)
            sondage.Votes = new List<Vote>();
        sondage.Votes.Add(vote);
        bdd.SaveChanges();
    }

    public List<Resultats> ObtenirLesResultats(int idSondage)
    {
        List<Resto> restaurants = ObtientTousLesRestaurants();
        List<Resultats> resultats = new List<Resultats>();
        Sondage sondage = bdd.Sondages.First(s => s.Id == idSondage);
        foreach (IGrouping<int, Vote> grouping in sondage.Votes.GroupBy(v => v.Resto.Id))
        {
            int idRestaurant = grouping.Key;
            Resto resto = restaurants.First(r => r.Id == idRestaurant);
            int nombreDeVotes = grouping.Count();
            resultats.Add(new Resultats { Nom = resto.Nom, Telephone = resto.Telephone, NombreDeVotes = nombreDeVotes });
        }
        return resultats;
    }

    public bool ADejaVote(int idSondage, string idStr)
    {
        int id;
        if (int.TryParse(idStr, out id))
        {
            Sondage sondage = bdd.Sondages.First(s => s.Id == idSondage);
            if (sondage.Votes == null)
                return false;
            return sondage.Votes.Any(v => v.Utilisateur != null && v.Utilisateur.Id == id);
        }
        return false;
    }

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

    private string EncodeMD5(string motDePasse)
    {
        string motDePasseSel = "ChoixResto" + motDePasse + "ASP.NET MVC";
        return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(ASCIIEncoding.Default.GetBytes(motDePasseSel)));
    }
}

Pourquoi je vous ai fait écrire une méthode pour récupérer un utilisateur à partir d’une chaîne ? Bande de curieux, je vous le dirai plus tard. ^^

Sinon, le code en lui-même n’est pas transcendant. Il ne faut pas oublier d’instancier les listes quand elles sont vides, notamment la liste de votes ; mais si vous ne l’avez pas fait, vous vous en êtes rendus compte en exécutant les tests. Puis, il faut savoir qu’on ne peut pas utiliser directement la méthode d’encodage MD5 dans une expression lambda d’Entity Framework (si vous avez rencontré cette erreur, le compilateur vous a surement aidé pour le corriger tout seul). Enfin, il ne faut pas oublier d’appeler la méthode SaveChanges  pour faire persister nos informations. Là encore, vous vous en serez rendu compte en lançant tous les tests.

Vous avez maintenant une belle DAL complète et un peu plus de pratique d’Entity Framework ; ce qui est parfait. 

Example of certificate of achievement
Example of certificate of achievement