تعلم ASP.NET MVC


الدرس: النموذج


الصفحة السابقة
لقد تحدثنا بالفعل قليلاً عن نموذج في الدراسة النظرية لـ MVC ، ثم في Hello World السريع. أذكرك أن النموذج يتوافق مع M of MVC . هذه هي البيانات وحالة تطبيق الويب ، وعادة ما تكون مجموعة من الفئات للوصول إلى بياناتك.

لقد أنشأنا في Hello World نموذجًا مبسطًا يحتوي على فئة Client   تتكون من خاصيتين بسيطتين تسمحان بتخزين اسمه وعمره. بشكل عام ، الفئات التي تحتوي على النموذج هي فئات POCO .
POCO تعني Plain Old CLR Object ويمثل فئة صغيرة للغاية مع خصائص فقط تسمح لنا باحتواء القيم المرتبطة بكائن.

عادة ، مع فئة عميل POCO ، يمكننا وضع اسمه وعمره ، ولكن أيضًا الاسم الأول ، ولماذا لا يكون حجمه وتاريخ ميلاده وما إلى ذلك ومع ذلك ، لا تحتوي هذه الفئة على طرق. Client  سيتم ملء كائن بشيء ويمكننا على سبيل المثال الحصول على قائمة العملاء. هذا ما فعلناه في Hello World ، لقد قمت بإنشاء فصل Clients   يحتوي على طريقة ObtenirListeClients   وأعاد لنا هذه القائمة مع البيانات الثابتة.
بشكل عام ، لا تتم كتابة البيانات مباشرة في ملفات C# لأنها يمكن أن تتطور ديناميكيًا. من الواضح أنني لن أقوم بتعديل تطبيق الويب الخاص بي في كل مرة يكون لدي عميل جديد ... في كثير من الأحيان ، سيتم تخزين بياناتنا في قاعدة البيانات. إنه ليس التزامًا ولكن قاعدة البيانات هي نظام متقدم للغاية الغرض منه هو تخزين البيانات بكفاءة.

عرض تطبيق الخيط الأحمر


لذا ، الكلام الكثير جيد ، ولكن عندما يكون المثال ملموسًا فإننا نفهم أفضل! بالفعل ، عندما نتحدث عن العملاء ، نرى على الفور كل شيء.

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

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

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

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

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

Entity Framework للنموذج


وبالتالي فإن النموذج هو جميع الفئات التي ستسمح لنا "بوضع نموذج" لتطبيقنا ، بالإضافة إلى الطبقة التي تسمح باستمرار المعلومات.

هناك العديد من الحلول لاستمرار البيانات ، لكنني سأقدم هنا كيف يمكننا حفظ هذه البيانات في قاعدة البيانات. هذه هي الحالة الأكثر كلاسيكية والأكثر فاعلية والحالة التي ستلتقي بها كثيرًا في العمل.
لهذا للوصول إلى البيانات ، سنستخدم قاعدة بيانات SQL Server المحلية ، والتي ستوفر لنا الكثير من تكوينات الخادم التي لا نريد التعامل معها في هذا البرنامج التعليمي.

يرجى ملاحظة أن قاعدة البيانات المحلية لا تستخدم في بيئة الإنتاج.
هناك العديد من الحلول للوصول إلى قاعدة البيانات ، هنا سنستخدم على سبيل المثال ORM كإطار عمل كيان لتسهيل هذا الوصول إلى البيانات.

يعني ORM في اللغة الإنجليزية  object-relational mapping ، والذي يمكن ترجمته بواسطة "رسم خرائط الكائنات العلائقية" . إنها تقنية برمجة حاسوبية تجعل من الممكن إنشاء مراسلات بين قاعدة بيانات علائقية وكائن نموذجي للفئات.
لن أصف إطار الكيان (Entity Framework) بتفصيل كبير. إذا كان الموضوع يهمك ، فلا تتردد في الاطلاع على دروس أخرى. هنا ، سنقوم ببساطة باستخدام إطار الكيان(Entity Framework) من خلال فهم ما نقوم به تقريبًا ، مع ترك جزء التحكم المتقدم في الأداة جانبًا.

نمذجة بياناتك وإنشاء قاعدة بيانات


سنبدأ بإنشاء مشروع جديد ، تطبيق MVC بالطبع! يمكننا تسميتها ChoixResto على سبيل المثال ، أو أي اسم تريده ؛ سيكون بالطبع مشروع "فارغ" . لا تتردد في تحديد المربع الذي يسمح بإنشاء مشروع اختبار ، سنحتاج إليه.

بشكل عام ، هناك عدة طرق لإنشاء طبقة الوصول إلى البيانات باستخدام Entity Framework :
  • عن طريق توليد نموذج من قاعدة بيانات. هذا ينطوي بالفعل على وجود قاعدة بيانات مع الجداول والعلاقات. مفيد جدًا إذا انضممت إلى فريق يعمل على تطبيق موجود. يطلق عليه قاعدة البيانات أولا .
  • من خلال إنشاء نموذج من مصمم Visual Studio . نحدد كيانات نموذجنا ثم العلاقات فيما بينها ، ويقوم Visual Studio تلقائيًا بإنشاء البرنامج النصي لقاعدة البيانات لإنشاء بنية قاعدة البيانات المطابقة للنموذج. يطلق عليه النموذج الأول (Database First) .
  • من خلال كتابة كود فئات كياناتنا بشكل مباشر. وبالمثل ، فإن Visual Studio قادر على إنشاء قاعدة البيانات المقابلة لنموذج الكائن لدينا . يطلق عليه اكود أولا (Model First)ً .
لاحظ أنني تحدثت فقط عن الإنشاء هنا ، ولكن كل هذا يعمل على تحديث النموذج ، في حالة إضافة جداول / كيانات ، وحذف العلاقات ، وما إلى ذلك.
لقد وصفت بالفعل في كتابي لتعلم كيفية تطوير تقنية Model First في C# ، وهنا سأقدم الكود أولاً. إذا كنت قد قرأت عملي بالفعل ، فيمكنك اكتشاف هذه الطريقة الأخرى للقيام بالأشياء. هنا ، لديها أيضًا ميزة كونها أسرع في إعداد وتقليل عدد لقطات الشاشة. عصفورين بحجر واحد. 
أول شيء يجب القيام به هو الرجوع إلى إطار الكيان. أسهل طريقة هي استخدام NuGet والانتقال مباشرةً إلى حزمة Entity Framework . للقيام بذلك ، انقر بزر الماوس الأيمن على مراجعك واختر إدارة حزم NuGet :
ASP.NET framework
إدارة حزم NuGet
يمكنك بعد ذلك البحث عن الحزمة وتثبيتها باستخدام الكلمة الرئيسية EntityFramework :
ASP.NET framework
الرجوع إلى إطار الكيان
يجب أن يقوم تطبيقنا بإدارة العديد من الكائنات ، وسوف نقوم بإنشائها في دليل النماذج. أضف أولاً فئة Utilisateur  . يجب أن يكون لكل مستخدم معرف تقني واسم أول:

public class Utilisateur
{
    public int Id { get; set; }
    public string Prenom { get; set; }
}
نحن بحاجة للتعامل مع المطاعم. قم بإنشاء الفصل التالي حيث يحتوي كل مطعم أيضًا على معرف تقني واسم ورقم هاتف:

public class Resto
{
    public int Id { get; set; }
    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; }
}
لاحظ أن بعض الخصائص مسبوقة بالكلمة الرئيسية virtual ، وهذا يسمح بتفعيل التحميل البطيء للممتلكات بواسطة Entity Framework من أجل السماح بتحميل هذه الكائنات من معرفاتها.
نحتاج الآن إلى سياق(context) للوصول إلى هذه البيانات. هذه فئة جديدة مشتقة من الفصل DbContext  ،

public class BddContext : DbContext
{
    public DbSet<Sondage> Sondages { get; set; }
    public DbSet<Resto> Restos { get; set; }
}
ستحتاج إلى تضمين مساحة الاسم التالية:

using System.Data.Entity;
نقطة الدخول لهذا السياق هي قائمة الاستطلاعات. لهذا ، سنستخدم الفئة  DbSet  العامة مع نوعنا Sondage  . أضفنا أيضًا نقطة دخول أخرى ، قائمة المطاعم.
هذا كل شيء ، لقد انتهى من الإصدار الأول من نموذجنا (ونعم ، الإصدار الأول لأننا (وخاصة أنت!) سنحسنه لاحقًا).

الوصول إلى النموذج


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

public interface IDal : IDisposable
{
    List<Resto> ObtientTousLesRestaurants();
}
هذه الواجهة بالطبع توضع في دليل الموديلات.
لاحظ أنني أستخدم هنا مباشرة في واجهتي فئة Resto وهي فئة النموذج الذي أنشأناه. إنها ممارسة شائعة مع Entity Framework لأن هذه الكائنات هي POCOs بسيطة يمكننا إعادة استخدامها حسب الرغبة.
ثم نحتاج إلى إنشاء فئة تطبق هذه الواجهة والتي ستهتم بإدراج المطاعم المختلفة المسجلة في قاعدة البيانات:

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();
    }
}
الأمر بسيط هنا. نستخدم الصف BddContext   للوصول إلى العناصر المختلفة لقاعدة البيانات. نرى أننا ننسخ هذا الكائن في مُنشئ DAL وأننا نحذفه بشكل صحيح عن طريق استدعاء طريقته Dispose  . والباقي بسيط للغاية ، لاسترداد قائمة جميع المطاعم ، نستخدم خاصية Restos   هذه الفئة.
الآن دعنا نضيف طريقة لإنشاء مطعم جديد ، في واجهتنا أولاً ، ثم تنفيذه في DAL :

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
}
لإنشاء مطعم جديد ، Resto ما عليك سوى إنشاء كائن وإضافته إلى قائمة المطاعم :

public class Dal : IDal
{
    [...]

    public void CreerRestaurant(string nom, string telephone)
    {
        bdd.Restos.Add(new Resto { Nom = nom, Telephone = telephone });
        bdd.SaveChanges();
    }
}
سنستدعي الطريقة SaveChanges   لاستمرار التعديلات في قاعدة البيانات.

اختبر النموذج


فكيف يمكنك التحقق من أن كل شيء يعمل؟
عن طريق اختبار بالطبع!
لذا يمكننا إنشاء تطبيق MVC صغير يمكن أن يطلق عليه DAL ... ولكن من الواضح أن هناك حلًا أنظف وأكثر استدامة. لقد خمنت ذلك: اختبارات تلقائية.
الآن بعد أن أصبحت خبيرًا في الاختبار ، لا شيء يمكن أن يكون أبسط.
إذا لم تكن قد قمت بذلك بالفعل ، قم بإضافة مشروع اختبار إلى الحل الخاص بك ChoixResto  ، والذي نسميه ChoiceResto.Tests .
وهكذا خلق طبقة جديدة من الاختبارات :  DalTests  .
سنقوم بتطوير اختبار يجعل من الممكن إنشاء مطعم والتحقق من وجوده من خلال استعادة جميع المطاعم. سهل:

 [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);
        }
    }
}
سيضيف أيضًا المرجع إلى Entity Framework كما فعلنا بالفعل لتطبيق الويب ، وذلك بفضل NuGet . يمكنك فعلها الآن!
ثم دعنا نجري الاختبار ... ها أنت ذا ، الاختبار أخضر ، كل شيء على ما يرام 
ASP.NET framework
يعمل الاختبار
ماذا؟ لا يعمل لك؟ يمكن أن يحدث للأسف. 
إذا كان الأمر كذلك ، فقد تظهر لك رسالة الخطأ التالية:
طرحت طريقة الاختبار [...] استثناء:
System.Data.ProviderIncomp CompatibleException : حدث خطأ أثناء استرداد معلومات الموفر من قاعدة البيانات. يمكن أن يحدث هذا بسبب Entity Framework الذي يستخدم سلسلة اتصال غير صحيحة. تحقق من الاستثناءات الداخلية لمزيد من المعلومات وتحقق من صحة سلسلة الاتصال. ---> System.Data.ProviderIncomp CompatibleException : لم يُرجع الموفر سلسلة ProviderManifestToken . . ---> System.Data.SqlClient.SqlException : حدث خطأ متعلق بالشبكة أو حدث خاص بالمثيل أثناء إنشاء اتصال بـ SQL Server . لا يمكن العثور على الخادم أو لا يمكن الوصول إليه. تحقق من صحة اسم المثيل ومن اعدادات SQL Server للسماح بالاتصالات البعيدة
(الموفر: واجهات شبكة SQL ، الخطأ: 26 - خطأ في تحديد موقع الخادم / المثيل المحدد)
لحل هذه المشكلة ، قم بتحرير ملف app.config لمشروع الاختبار الخاص بك لتضمين:

<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>
نعم ، سلسلة الاتصال. لماذا يعمل أحيانًا بدونه وأحيانًا معه؟ إذا كنت لا تمانع ، سأتحدث عنه مباشرة بعد ذلك .
إذا لم يكن ملف app.config موجودًا ، فانقر بزر الماوس الأيمن على مشروع الاختبار وأضف عنصرًا جديدًا. اختر ملف التكوين وتحقق من أنه يسمى app.config . ثم أضف سلسلة الاتصال التي رأيناها للتو.
على أي حال ، مع ذلك ، يجب أن يمر الاختبار الخاص بك الآن.

أين بياناتي؟


حسنًا ، لقد كنا نتحدث عن قاعدة بيانات لفترة من الوقت ، ولكن أين هي؟
في الواقع ، كان إطار الكيان هو الذي أنشأه لنا. بموجب الاتفاقية ، عندما نستخدم نهج Code First كما فعلنا للتو ، فإن Entity Framework مسؤول عن إنشاء قاعدة البيانات (إن لم تكن موجودة).
يحاول أولاً الاتصال بـ SQL Server Express . إذا نجح ، فإنه يخلق القاعدة هناك. إذا لم تنجح ، فستحاول الاتصال بقاعدة البيانات المحلية (LocalDb) لإنشاء قاعدة البيانات هناك.
بينما قمنا بتثبيت 2019 Visual Studio ، بشكل افتراضي لدينا قاعدة البيانات المحلية (LocalDb) . ومع ذلك ، فإن أولئك الذين لديهم Visual Studio 2010 لديهم عادةً خيار تثبيت Sql Server Express .
من خلال الاتصال بقاعدة البيانات المحلية ، يتم الخلط بين 2019 Visual Studio أحيانًا. إذا فشل الاختبار الخاص بك في المرة الأولى ، فذلك لأنه حاول الاتصال بـ SQL Server Express في المقام الأول ولم ينجح. نقول له بسلسلة الاتصال هذه حيث يجب أن يتصل ، في هذه الحالة على localdb .
إلى جانب ذلك ، سنقوم بالاتصال بها لمعرفة ما حدث في قاعدة البيانات الشهيرة هذه. للقيام بذلك ، انتقل إلى مستكشف الخادم وانقر على اتصال بقاعدة البيانات:
ASP.NET framework
الاتصال بقاعدة البيانات المحلية
إذا لم يتم وضع مصدر البيانات على خادم Microsoft Sql (SqlClient) ، فسيتعين عليك النقر فوق تعديل لاختيار هذا:
ASP.NET framework
تحرير مصدر البيانات
ثم حدد "Microsoft Sql Server":
ASP.NET framework
خادم SQL كمصدر بيانات
مرة أخرى في نافذة إضافة اتصال ، أدخل عنوان الخادم وقاعدة البيانات. العنوان هو (localdb) \ v11.0 وسيكون اسم قاعدة البيانات هو ChoixResto.Models.BddContext :
ASP.NET framework
الاتصال على الخادم المحلي بقاعدة البيانات
إذا كنت تستخدم SQL Server Express ، فستحتاج إلى استخدام اسم المثيل الخاص بك ، وعادة ما يكون.\SQLEXPRESS . ( "." استبدال اسم جهازك) .
لاحظ أن قاعدة البيانات تسمى نفس اسم فئة السياق الخاصة بنا ، مسبوقة باسم المشروع.
انقر فوق موافق وستتمكن من رؤية قاعدة البيانات الخاصة بنا. إذا فتحت الشجرة واستعرضت الجداول ، فسترى ما يلي:
ASP.NET framework
جداول قاعدة البيانات
هذه هي جميع الجداول التي تم إنشاؤها بواسطة Entity Framework . إنها تعكس نموذج بياناتنا بشكل مثالي.
جدول Restoes  يمثل مطاعمنا بمعرف يعمل كمفتاح أساسي واسم وهاتف.
ستوافق على أن الاسم Restoes   ليس رائعًا ... سنرى كيفية إصلاحه.
لا تنتبه إلى الجدول __MigrationHistory   الذي يعد جدول ترحيل يستخدمه الرمز أولاً ، علاوة على ذلك ، قد لا يكون لديك حتى.
يحتوي الجدول Utilisateurs  على معرف واسم أول. يحتوي جدول الاقتراع على معرف وتاريخ. ولكن أين ذهبت قائمة الأصوات المرتبطة باستطلاعاتنا؟ إنه في الجدول Votes  . نرى أن التصويت له معرف ولكن أيضًا معرف المطعم والمستخدم والاستطلاع. هكذا تصنع العلاقات.
ماذا عن معرفاتنا؟
في الواقع ، إنها تلقائية ، أي أنها تتزايد ذاتيًا وفريدة من نوعها. يمكننا رؤيته على سبيل المثال عن طريق تحديد الحقل Id  من الجدول Utilisateurs  . لدينا الخصائص التالية:
ASP.NET framework
هيكل حقل المعرف في جدول المستخدمين
يمكننا أن نرى أن هذا المعرف هو "هوية(Id)" ، وهذا يعني أنه فريد ومتزايد ذاتيًا. إلى جانب الزيادة هي 1. يمكنك بنفس الطريقة رؤية تفاصيل الخصائص الأخرى إذا كانت تهمك.
كيف قرر إطار عمل الكيان من تلقاء نفسه أن خاصية معرّفنا كانت المفتاح الأساسي الذي يتزايد بشكل ذاتي؟ وكيف قرر أن العلاقات تمت على معرف الكيانات؟
بكل بساطة بفضل اتفاقيات إطار العمل. بشكل افتراضي ، إذا قمت بتسمية خاصية ) Id  أو (ID أو إذا كان  NomDeLaClasseId  فان Entity Framework يدرك بنفسه أنك تريد أن تكون هذه الخاصية هي المفتاح الأساسي للكيان الخاص بك.
إنها اتفاقية توفر الوقت وتبسط مهمة المطور. ولكن كن مطمئنًا ، على الرغم من كل شيء لا تريد أن يتم استدعاء معرفك Id  أو أن يكون المفتاح الأساسي معرفًا غير رقمي ، فهناك دائمًا طريقة للقيام بذلك ، سنرى ذلك أكثر قليلاً.
الآن انقر بزر الماوس الأيمن على قاعدة البيانات الخاصة بك واختر تقديم طلب جديد:
ASP.NET framework
افتح طلبًا جديدًا
تظهر نافذة جديدة تسمح لنا بعمل استعلام SQL ، وهي لغة الاستعلام عن قواعد البيانات. لن أقوم بتدريسك درسًا على SQL لأننا سنخرج مباشرة من الدرس ، لذا إليك كل ما تم طهيه لمعرفة ما في طاولة المطعم:

select * from Restoes
إذا ضغطت على المثلث الصغير لتنفيذ الاستعلام ، فستتمكن من رؤية نتائجه:
ASP.NET framework
نتيجة استعلام SQL
وهي محتويات الجدول Restoes  . لدينا معرّف تزايدي ذاتيًا: 1 ​​، اسم المطعم ورقم الهاتف
وهنا تكون بياناتنا ... مثالية.

إعادة تعيين قاعدة البيانات


 ماذا لو أعدنا اختبارنا؟ اختبارنا هو ما يسمح لنا أن نكون على يقين من أن كودنا يعمل. لذلك يجب أن تعمل لأننا لم نلمس الكود ...
ترى مثلي؟ ليس لدي وجه؟
ASP.NET framework
فشل الاختبار
الاختبار أحمر ، اللعنة ، اللعنة!
فشل Assert.AreEqual . متوقع 1 وفي الحقيقة لدينا 2. وهو على المحك:

Assert.AreEqual(1, restos.Count);
نعم ... في الواقع ، يهدف اختبارنا إلى إنشاء مطعم والتحقق من إنشائه. لذلك ، في المرة الأولى التي بدأنا فيها الاختبار ، أضاف المطعم ، الاختبار على ما يرام لأنه يوجد مطعم واحد فقط. في المرة الثانية ، يوجد مطعمان مرتين. لذلك فشل الاختبار.
للتحقق من ذلك ، يمكنك إعادة تنفيذ استعلام SQL ومعرفة أنه يقوم بإرجاع سطرين ، علاوة على ذلك يمكننا أن نرى في تمرير تلك id  التي لديها الزيادة الذاتية.
لدينا مشكلة في الواقع لأن الاختبارات التلقائية يجب أن تكون بلا دولة حتى تتمكن من العمل إلى أجل غير مسمى. ومع ذلك ، فإن قاعدة البيانات هي على وجه التحديد شيء يهدف إلى الحفاظ على استمرار الحالات. لذلك يجب علينا إيجاد حل للتحكم في حالة بدء اختباراتنا.
هناك العديد من الحلول:
  • يمكننا التأكد من أن اختباراتنا لا تزعج حالة قاعدة البيانات ، وهذا يعني على سبيل المثال أنه في كل مرة نضيف فيها عنصرًا ، نحذفه بعد ذلك. هذه التقنية سهلة التنفيذ ولكنها تثير مشكلة إذا كان هناك خطأ أثناء الحذف. سنجد أنفسنا في حالة غير منضبطة.
  • يمكنك إعادة تعيين القاعدة إلى الصفر في كل مرة تقوم فيها بإجراء اختبار. هذا يعني على سبيل المثال أننا نحذف قاعدة البيانات قبل كل اختبار ، وأننا نقوم بإعادة إنشائها وأننا ندرج (ربما) مجموعة بيانات رئيسية لاختباراتنا. يتضمن هذا تطويرًا إضافيًا وإنشاء مجموعة بيانات ليس من السهل دائمًا إذا كانت البيانات المراد اختبارها معقدة. يتطلب أيضًا تخصيص قاعدة بيانات للاختبارات والحصول على أخرى للعروض التوضيحية على سبيل المثال.
  • يمكننا أيضًا استخدام الأطر التي تنفذ الاختبارات في المعاملات التي سيتم إلغاؤها بعد تنفيذ كل اختبار ، مثل ndbunit .
سنستخدم الحل الثاني هنا والذي يتناسب تمامًا مع حالتنا ، نظرًا لأننا سنحتاج إلى مجموعة بيانات مخفضة ، ويمكننا بسهولة استخدام العديد من قواعد البيانات.
في الواقع ، لإنشاء قاعدة بيانات جديدة ، لا شيء يمكن أن يكون أبسط. ما عليك سوى تعديل (أو إضافة إذا كنت محظوظًا بأن الاختبار يعمل في المرة الأولى) سلسلة الاتصال في مشروع الاختبار الخاص بنا ، الموجود في ملف app.config . استخدم سلسلة الاتصال الجديدة هذه على سبيل المثال:

<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>
لاحظ أن قاعدة البيانات تسمى الآن BddContextTest . شغّل الاختبار مرة أخرى ، حيث يمر دون مشكلة لأنه تم إنشاء قاعدة بيانات جديدة. يمكنك التحقق من ذلك عن طريق تسجيل الدخول ، كما فعلنا من قبل.
ASP.NET framework
قاعدة الاختبار الجديدة
يرجى ملاحظة أنه في قاعدة بيانات تطبيق الويب الخاص بنا ، ChoixResto.Models.BddContext ، يوجد الآن مطعمان يطلق عليهما نفس الاسم. أشجعك على حذف محتويات الجدول باستخدام الاستعلام التالي:

delete from Restoes
حسنًا ، هذا جيد ، لكننا ننتهي بالمشكلة نفسها تمامًا ، ولكن هذه المرة على أساس الاختبارات. لذلك عليك التأكد من إعادة تعيين قاعدة البيانات. لهذا ، نستخدم الأمر السحري التالي:

IDatabaseInitializer<BddContext> init = new DropCreateDatabaseAlways<BddContext>();
Database.SetInitializer(init);
init.InitializeDatabase(new BddContext());
يشير هذا الأمر السحري إلى أننا نريد إعادة تعيين قاعدة البيانات الخاصة بنا عن طريق حذفها (Drop) ثم إعادة إنشائها في كل مرة (CreateDatabaseAlways) .
لذلك لدينا اختبار لدينا والذي سيكون:

 [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);
    }
}
وها أنت ، يمكنك تشغيل الاختبار إلى ما لا نهاية الآن. يحدث في كل مرة. الجانب السلبي هو أن إعادة تعيين القاعدة تستغرق وقتًا. بالنسبة لي ، يستغرق الاختبار ما بين 10 و 15 ثانية أو نحو ذلك ، والتي لا تزال طويلة نسبيًا ، خاصة إذا كان لديك الكثير من الاختبارات ... ولكن هذا هو الثمن الذي يجب دفعه بهذه الطريقة. إلى جانب ذلك ، لسنا في غضون دقائق للتأكد من أن تطبيقنا يعمل.
لاحظ أنك قد تحصل على خطأ إذا كانت قاعدة البيانات قيد الاستخدام بالفعل ، مثل:
أسلوب الاختبار [...] طرح استثناء:
System.Data.SqlClient.SqlException : لا يمكن إسقاط قاعدة البيانات "ChoixResto.Models.BddContextTest" لأنها قيد الاستخدام حاليًا.
في هذه الحالة ، لا تتردد في إغلاق الاتصالات التي قمنا بفتحها عبر مستكشف قاعدة البيانات ، أو حتى إعادة تشغيل Visual Studio وسيكون كل شيء على ما يرام.
هيا ، نصيحة صغيرة قبل الانتهاء من هذا القسم. إذا أنشأنا اختبارًا جديدًا (لن تفشل في إجرائه ) لاختبار عناصر أخرى ، فيجب علينا مرة أخرى استدعاء الطريقة التي تعيد تعيين قاعدة البيانات في بداية طريقة الاختبار.
إذا كانت جميع طرق الاختبار الخاصة بك بحاجة إلى استدعاء هذه الطريقة الشهيرة ، فيمكننا استخدام إطار الاختبار للقيام بذلك لنا ، من خلال تزيين طريقة بالسمة TestInitialize ، والتي تعطي:

 [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);
        }
    }
}
سيتم استدعاء الطريقة المزينة بهذه السمة قبل كل اختبار ، وهي المكان المثالي لإجراء إعادة تعيين قاعدة البيانات الخاصة بنا.
لاحظ أن المكافئ للحصول على طريقة يتم استدعاؤها بعد كل اختبار هي السمة TestCleanup  .

شروح


هل تتذكر الجدول Restoes  ؟ آه هناك ، ما الاسم ... بينما تم تسمية جميع الجداول الأخرى بشكل صحيح ، هنا قام Entity Framework بعمل أي شيء لنا ... في الواقع ، حاول Entity Framework جمع اسم الجدول ، باستثناء أنه فعل ذلك باللغة الإنجليزية Resto  . 
ولكن كن مطمئنًا ، من الممكن التأثير على التوليد التلقائي لإطار الكيان ، بحيث يستخدم ما نريد ، بدلاً من اصطلاحاته.
على سبيل المثال ، لإجبارها على استدعاء جدول Restos ، يمكنني تزيين الفئة بالسمة Table  :

 [Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    public string Nom { get; set; }
    public string Telephone { get; set; }
}
لهذا ، يجب أن نضيف مساحة الاسم:

using System.ComponentModel.DataAnnotations.Schema;
ومن هنا ، فإن جدولي يحمل الاسم المطلوب:
ASP.NET framework
يحتوي جدول Restos الآن على الاسم الصحيح
هذه السمات التي تسمح لنا بالتأثير على سلوك توليد نموذجنا تسمى DataAnnotations . هناك العديد من الآخرين ، لن نتمكن من رؤيتهم جميعًا هنا.
تذكر ، على سبيل المثال ، المفاتيح الأساسية. من الممكن إعطاء اسم آخر أو نوع آخر لمفتاح أساسي. في هذه الحالة ، تحتاج فقط إلى تزيين خاصية الفئة للسمة [Key] ، على سبيل المثال:

 [Table("Restos")]
public class Resto
{
    [Key]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}
لكن لا تفعل ذلك لفئتنا Resto  . أشجعك هنا على الحفاظ على المفتاح الأساسي للزيادة الذاتية ؛ من يدري إذا لم يكن هناك مطعمان بنفس الاسم؟ إلى جانب ذلك ، من الممارسات السيئة استخدام اسم كمفتاح أساسي. إذا أردنا تغيير اسم المطعم؟ سنضطر إلى حذف السجل ثم إعادة إنشائه ، بدلاً من تحديثه فقط.
هناك أيضًا التعليق التوضيحي [ Required ] ، الذي سيجعل الملكية إلزامية. على سبيل المثال في مطعم ، سيكون من الإلزامي الحصول على اسم ، من ناحية أخرى يمكن للمرء الاستغناء عن رقم هاتف إذا لم يقبل بالحجز أبدًا.
إليك ما يبدو عليه:

 [Table("Restos")]
public class Resto
{
    public int Id { get; set; }
    [Required]
    public string Nom { get; set; }
    public string Telephone { get; set; }
}
إلى جانب ذلك ، سنفعل الشيء نفسه مع الاسم الأول للمستخدم:

public class Utilisateur
{
    public int Id { get; set; }
    [Required]
    public string Prenom { get; set; }
}
لاحظ أنه سيتم استخدام هذا التعليق التوضيحي أيضًا لاحقًا ، عندما ندرس العرض وسنحتاج إلى التحقق من النماذج. القليل من الصبر ، سنكتشف لاحقًا.
في قاعدة البيانات ، سيقوم هذا التعليق التوضيحي بتعيين الحقل الخاص بك إلى قيمة غير فارغة:
ASP.NET framework
وبعد استخدام السمة المطلوبة
لاحظ أيضا سمات MaxLength  و MinLength  التي تحد من حجم الحقل . على سبيل المثال ، يمكننا استخدامه على الاسم الأول للمستخدم للإشارة إلى أنه يجب ألا يتجاوز 80 حرفًا. لماذا 80؟ حسنًا ، لا أعلم ، عشوائيًا  :

public class Utilisateur
{
    public int Id { get; set; }
    [Required, MaxLength(80)]
    public string Prenom { get; set; }
}
يمكنك حذف التعليق التوضيحي MaxLength   من الاسم الأول ، ولن نستخدمه.
لاحظ أنه عندما نجمع عدة تعليقات توضيحية ، يمكننا كتابتها على سطر واحد.
هيا ، أتوقف عند هذا التعليق التوضيحي ، واعلم أن هناك آخرين إذا كنت تريد الذهاب والحفر قليلاً على الإنترنت.
يمكنك أن ترى ، إذا قمت بإعادة تشغيل الاختبارات ، أن Entity Framework يدعم تعديلات النموذج بشكل جيد للغاية ، وكذلك تعديل هيكل قاعدة البيانات. لن أتحدث عنه هنا ، ولكن إذا كنت بحاجة إلى ذلك ، فلا تتردد في الاطلاع على البرامج التعليمية حول هذا الموضوع.

تحرير البيانات


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

public interface IDal : IDisposable
{
    void CreerRestaurant(string nom, string telephone);
    void ModifierRestaurant(int id, string nom, string telephone);
    List<Resto> ObtientTousLesRestaurants();
}
للتنفيذ ، إنه في الواقع سهل للغاية. تحتاج فقط إلى العثور أولاً على المطعم باستخدام هذا المعرف وتعديله وتطبيق التغييرات:

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();
    }
}
يبقى كتابة اختبار صغير للتحقق من أن كل شيء يعمل:

 [TestMethod]
public void ModifierRestaurant_CreationDUnNouveauRestaurantEtChangementNomEtTelephone_LaModificationEstCorrecteApresRechargement()
{
    using (IDal dal = new Dal())
    {
        dal.CreerRestaurant("La bonne fourchette", "01 02 03 04 05");
        List 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);
    }
}
ها أنت ذا. نبدأ بإنشاء مطعم ، لأن قاعدتنا فارغة ؛ ثم نحصل على معرف هذا المطعم. باستخدام هذا المعرف ، يمكننا استدعاء طريقة التعديل ، ثم التحقق من أن كل شيء على ما يرام.
ملحوظة: نظرًا لأننا بارعون في قاعدة بيانات الاختبار ، فيمكننا الاستغناء عن تحديد معرّف المطعم حيث يمكننا التنبؤ به بسهولة ، نظرًا لأنه سيكون بقيمة 1.
وبالتالي يصبح كود طريقة الاختبار:

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

باختصار


  • يتكون النموذج من فئات تسمح بالوصول إلى بيانات تطبيقنا.
  • يمكننا استخدام Entity Framework لإنشاء نموذجنا وإدارة ثبات قاعدة البيانات الخاصة به.
  • باستخدام 2019 Visual Studio ، يمكننا بسهولة إنشاء قاعدة بيانات محلية واستخدامها.
  • من الممكن العمل على الإنشاء التلقائي لقاعدة البيانات بفضل التعليقات التوضيحية التي تأخذ شكل السمات التي تزين النموذج.
  • من الممكن التحكم في حالة اختبارات قاعدة البيانات التلقائية عن طريق إعادة تعيين قاعدة البيانات في بداية كل اختبار.