تعلم ASP.NET MVC


الدرس: TP: أكمل النموذج


الصفحة السابقة
لقد بدأنا في تطبيق نموذجنا ولكن لم ينته تمامًا. وخمن ماذا؟ أنت الذي ستكمله من خلال نقطة اتصال صغيرة ستسمح لك بالتحقق من أنك قد فهمت كل شيء عن النموذج.
وبعد ذلك ، سأكون متأكدًا من أنني لست الوحيد الذي يسخن يديه. 

تعليمات للعمل العملي


حسنًا ، لقد فهمت الفكرة. سأطلب منك إكمال نموذجنا و DAL لدينا. إليك ما لدينا حتى الآن:
الجانب Utilisateur  :

public class Utilisateur
{
    public int Id { get; set; }
    [Required]
    public string Prenom { get; set; }
}
الجانب Resto :

 [Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    [Required]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}
للتصويت:

public class Vote
{
    public int Id { get; set; }
    public virtual Resto Resto { get; set; }
    public virtual Utilisateur Utilisateur { get; set; }
}
والاستطلاعات:

public class Sondage
{
    public int Id { get; set; }
    public DateTime Date { get; set; }
    public virtual List<Vote> Votes { get; set; }
}
فئة السياق هي:

public class BddContext : DbContext
{
    public DbSet<Sondage> Sondages { get; set; }
    public DbSet<Resto> Restos { get; set; }
}
وفي الوقت الحالي ، فإن واجهة DAL هي:

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    void ModifierRestaurant(int id, string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
}
ثم تنفيذه:

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();
    }
}
يبقى تحسين هذا النموذج للحصول على نموذج أعلى!
مرة واحدة ليست مخصصة ، لتحقيق هذا TP ، ستفعل TDD (تطوير مدفوع باختبار) . لقد كتبت لك الاختبارات التي أود أن أراها مع العديد من الحالات الاسمية. انسخها إلى الحل الخاص بك وتأكد من نجاحها جميعًا.  إليك فئة الاختبار الكاملة:

 [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);
    }
}
بالطبع ، بعد نسخ هذا الفصل ولصقه ، لن يتم تجميع مشروعك بعد الآن. أول شيء يجب القيام به هو إضافة الأساليب في واجهة DAL ، ثم تنفيذ الأساليب. سيكون هناك أيضا نموذج لإكماله.
هل تريد الذهاب بدون تعليمات إضافية؟ لذلك لا تتردد وتخطي القسم التالي! خلاف ذلك ، يمكنك الذهاب لإلقاء نظرة سرية دون أن ينظر إليك أي شخص. 

بعض التفاصيل الإضافية


من أول الأشياء التي ستراها هو أنني أجريت عدة تغييرات على النموذج. على سبيل المثال ، أضفت كلمة مرور في فئة المستخدم ، والتي يجب أن تكون إلزامية.
أضفت أيضًا فئة نتائج. حتى إذا كنت تستطيع تخمين محتواه من خلال النظر في الاختبارات ، فأضيفه إليك هنا بحيث يمكنك إضافته في النموذج:

public class Resultats
{
    public string Nom { get; set; }
    public string Telephone { get; set; }
    public int NombreDeVotes { get; set; }
}
لاحظ أن هذه الفئة هي فئة حسابية ولا يجب استمرارها في قاعدة البيانات. لذا ... لا يجب أن تظهر في الصف BddContext ولا سيما كعضو DbSet  .
بالمناسبة ، BddContext  يجب تغيير هذه الفئة لإضافة وصول مباشر إلى المستخدمين.
بعد ذلك ، تتحدث الأساليب عن نفسها. سيتعين عليك إنشاء طريقة لإضافة مستخدم:

int AjouterUtilisateur(string nom, string motDePasse);
ثم آخر يسمح بمصادقته:

Utilisateur Authentifier(string nom, string motDePasse);
من الواضح أنك لن تخزن كلمة المرور بشكل واضح وستستخدم نظام تشفير MD5 لحفظ كلمة المرور. فيما يلي طريقة الترميز في MD5 :

private string EncodeMD5(string motDePasse)
{
    string motDePasseSel = "ChoixResto" + motDePasse + "ASP.NET MVC";
    return BitConverter.ToString(new MD5CryptoServiceProvider().ComputeHash(ASCIIEncoding.Default.GetBytes(motDePasseSel)));
}
يرجى ملاحظة أن نظام التشفير هذا غير كامل ويمكن أن يكون أكثر تعقيدًا. يجب ألا يظهر الملح الذي أضفته بوضوح في الكود. سيكون من الأفضل إذا تم تخزينه على سبيل المثال في machine.config . في إطار عملنا العملي ، سيكون هذا كافياً.
يمكنك إنشاء طريقتين للحصول على مستخدم من معرفه ، الإصدار ذي العدد الصحيح كمُدخل والإصدار بسلسلة كمُدخل يجب تحويله إلى عدد صحيح:

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);
حان دورك للعب الآن. لديك كل ما تحتاجه لجعلنا DAL لطيفًا سيجعل كل هذه الاختبارات خضراء ، بما يكفي لجعل العملاق الذي يحمل نفس الاسم غيورًا.
حظا سعيدا. 

الاصلاح


قد يبدو ذلك طويلًا لأنني كنت مطولًا بدلاً من ذلك في البيان ، ولكن في الواقع ، كان الأطول هو كتابة الاختبارات ، وكان أنا من فعل ذلك.  لذا إذا كان طولك يثبط عزيمتك ، فارجع إليها وأعطني هذه الاختبارات.
خلاف ذلك ، بالنسبة للشجاعة ، أنا متأكد من أنك نجحت في التراجع ، ليس من دون التعثر على بضع نقاط صغيرة. ولكن لا شيء مستحيل جداً. أنا فخور بكم.
على أي حال ، هذا هو تصحيحي لهذا الاختبار. ها هي أولاً كائنات النماذج المعدلة.
الفصل Utilisateur   بكلمة المرور الخاصة به:

public class Utilisateur
{
    public int Id { get; set; }
    [Required]
    public string Prenom { get; set; }
    [Required]
    public string MotDePasse { get; set; }
}
فئة النتائج التي رأيتها بالفعل ، إلا إذا تخطيت الفصل السابق:

public class Resultats
{
    public string Nom { get; set; }
    public string Telephone { get; set; }
    public int NombreDeVotes { get; set; }
}
الفئة BddContext مع خاصية المستخدمين الخاصة بها:

public class BddContext : DbContext
{
    public DbSet<Sondage> Sondages { get; set; }
    public DbSet<Resto> Restos { get; set; }
    public DbSet<Utilisateur> Utilisateurs { get; set; }
}
ثم الواجهة:

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);
}
وتنفيذها:

public class Dal : IDal
{
    private BddContext bdd;

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

    public List 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();
        sondage.Votes.Add(vote);
        bdd.SaveChanges();
    }

    public List ObtenirLesResultats(int idSondage)
    {
        List restaurants = ObtientTousLesRestaurants();
        List resultats = new List();
        Sondage sondage = bdd.Sondages.First(s => s.Id == idSondage);
        foreach (IGrouping 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)));
    }
}
لماذا أجعلك تكتب طريقة للحصول على مستخدم من سلسلة؟ حفنة من الفضول ، سأخبرك لاحقًا. 
خلاف ذلك ، فإن الكود نفسه ليس متجاوزا. لا تنسى إنشاء القوائم عندما تكون فارغة ، لا سيما قائمة التصويت ؛ ولكن إذا لم تفعل ذلك ، فقد اكتشفت من خلال إجراء الاختبارات. بعد ذلك ، كن على علم أنه لا يمكنك استخدام طريقة ترميز MD5 مباشرة في تعبير Lamda لإطار عمل الكيان (إذا واجهت هذا الخطأ ، فربما ساعدك المترجم على تصحيحه بنفسك). أخيرًا ، يجب ألا ننسى استدعاء الأسلوب SaveChanges   لاستمرار معلوماتنا. مرة أخرى ، ستدرك ذلك عن طريق إجراء جميع الاختبارات.
لديك الآن DAL كامل لطيف وممارسة أكثر قليلاً من إطار الكيان ؛ وهو مثالي.