Skip to content

9. التطبيق [SimuPaie] – الإصدار 5 – ASP.NET / خدمة الويب


قراءات موصى بها: مرجع [2]، مقدمة إلى C# 2008، الفصل 10 "خدمات الويب"


9.1. البنية الجديدة للتطبيق

البنية الطبقية لتطبيق Pam هي حاليًا كما يلي:

سنقوم بتطويرها على النحو التالي:

بينما في البنية السابقة، كانت الطبقات [web] و [metier] و [dao] تعمل في نفس الجهاز الظاهري.NET، في البنية الجديدة، ستعمل الطبقة [web] في آلة افتراضية أخرى غير الطبقات [metier] و [dao]. سيكون هذا هو الحال على وجه الخصوص إذا كانت الطبقة [web] موجودة على آلة M1 والطبقات [metier] و [dao] على آلة M2. لدينا هنا بنية عميل/خادم:

  • يتكون الخادم من الطبقتين [metier] و [dao]. ولأنه خدمة ويب، فإنه يحتاج إلى خادم الويب رقم 2 للتشغيل.
  • يتكون العميل من الطبقة [web]. للتشغيل، يحتاج إلى خادم الويب رقم 1.
  • يتواصل العميل والخادم عبر شبكة TCP/IP باستخدام بروتوكول HTTP / SOAP. ولهذا الغرض، يجب إضافة طبقتين جديدتين إلى البنية:
    • الطبقة [S] التي ستكون خدمة ويب. تستقبل خدمة الويب طلبات العملاء البعيدين وتستخدم الطبقتين [metier] و [dao] لتلبية هذه الطلبات. هناك العديد من الطرق لإنشاء خدمة TCP/IP. تتميز خدمة الويب بميزتين:
      • فهي تستخدم بروتوكول HTTP الذي تسمح به جدران الحماية الخاصة بالشركات والإدارات
      • تستخدم بروتوكول فرعي قياسي HTTP / SOAP، يتم تنفيذه بواسطة العديد من منصات التطوير: .Net، Java، Php، Flex، ... وبالتالي، يمكن "استهلاك" خدمة الويب (وهذا هو المصطلح الشائع) من قبل عملاء .Net و Java و Php و Flex، ...
    • الطبقة [C] التي ستكون عميل خدمة الويب البعيدة. وسيكون دورها هو التواصل مع خدمة الويب [S].

يمكن اشتقاق هذه البنية الجديدة من البنى السابقة دون بذل الكثير من الجهد:

  • تظل الطبقتان [metier] و [dao] دون تغيير
  • تتطور الطبقة [web] بشكل طفيف، وذلك أساسًا للإشارة إلى كيانات مثل Employe و FeuilleSalaire التي أصبحت كيانات في طبقة العميل [C]. هذه الكيانات مشابهة لتلك الموجودة في الطبقات [metier] أو [dao] لكنها تنتمي إلى مساحات أسماء مختلفة.
  • طبقة الخادم [S] هي فئة تنفذ واجهة IPamMetier لطبقة [metier]. يقتصر هذا التنفيذ على استدعاء الطرق المقابلة في طبقة [metier]. سيتم "عرض" الطرق التي تنفذها طبقة الخادم [S] على العملاء البعيدين الذين سيتمكنون من استدعائها.
  • سيتم إنشاء طبقة العميل [C] بواسطة Visual Studio.

مبادئ البنية الجديدة هي كما يلي:

  • تستمر الطبقة [web] في التواصل مع الطبقة [metier] كما لو كانت محلية. ولهذا الغرض، تنفذ طبقة العميل [C]واجهة IPamMetier للطبقة الفعلية [metier] وتظهر للطبقة [web] كطبقة [metier] محلية. بصرف النظر عن مشكلة مساحات الأسماء المذكورة سابقًا، لا تتغير الطبقة [web]. هذه هي ميزة العمل بالطبقات. لو كنا قد أنشأنا تطبيقًا أحادي الطبقة، لكان علينا إعادة تصميمه بشكل جذري.
  • تقوم طبقة العميل [C]، بشكل شفاف بالنسبة لطبقة [web]، بنقل طلبات هذه الأخيرة إلى خدمة الويب البعيدة [S]. وهي تتولى كل ما يتعلق بـ "الاتصال الشبكي". وتتلقى استجابة من خدمة الويب البعيدة، وتقوم بتنسيقها لتقديمها إلى الطبقة [web] بالشكل الذي تتوقعه هذه الأخيرة.
  • على جانب الخادم، تتلقى خدمة الويب [S] أوامر من عملائها البعيدين. تقوم بتنسيقها من أجل استدعاء أساليب واجهة IPamMetier للطبقة [metier]. وعندما تتلقى الرد من الطبقة [metier]، تقوم بتنسيقه لإرساله عبر الشبكة إلى العميل [C]. لا يلزم تعديل الطبقتين [metier] و [dao].

9.2. مشروع Visual Web Developer لخدمة الويب

نقوم بإنشاء مشروع جديد باستخدام Visual Web Developer:

  • في [1]، نختار مشروع ويب بلغة C#
  • في [2]، نختار "تطبيق خدمة الويب ASP.NET"
  • في [3]، نسمي مشروع الويب
  • في [4]، نحدد موقعًا لهذا المشروع
  • في [1] المشروع الذي تم إنشاؤه. إنه مشروع ويب كلاسيكي مع التفاصيل التالية:
    • تم تحديد أن المشروع من نوع "خدمة ويب". لا ترسل خدمة الويب صفحات ويب HTML إلى عملائها بل ترسل بيانات بتنسيق XML. لذلك لم يتم إنشاء الصفحة [Default.aspx] التي يتم إنشاؤها عادةً.
  • في [2]، تم إنشاء ملف [Service1.asmx] بالمحتوى التالي:

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5_webservice.Service1" %>
  • (تابع)
    • - تشير العلامة WebService إلى أن [Service.asmx] هي خدمة ويب
    • - يشير السمة CodeBehind إلى موقع شفرة المصدر لهذه الخدمة الويب
    • - يشير السمة Class إلى اسم الفئة التي تنفذ خدمة الويب في شفرة المصدر

كود المصدر [Service.asmx.cs] لخدمة الويب التي تم إنشاؤها افتراضيًا هو التالي:


using System.Web.Services;

namespace pam_v5_webservice
{
  /// <summary>
  /// وصف موجز لـ Service1
  /// </summary>
  [WebService(Namespace = "http://tempuri.org/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  // للسماح باستدعاء خدمة الويب هذه من برنامج نصي باستخدام ASP.NET AJAX، قم بإزالة علامات التعليق من السطر التالي. 
  // [System.Web.Script.Services.ScriptService]
  public class Service1 : System.Web.Services.WebService
  {

    [WebMethod]
    public string HelloWorld()
    {
      return "Hello World";
    }
  }
}
  • السطر 8: التعليق التوضيحي WebService الذي يؤدي إلى عرض الفئة Service1 الموجودة في السطر 13 كخدمة ويب. تنتمي خدمة الويب إلى مساحة أسماء لتجنب وجود خدمتين على الويب تحملان نفس الاسم. سنضطر إلى تغيير مساحة الأسماء هذه لاحقًا.
  • السطر 13: الفئة Service1 مشتقة من الفئة WebService في إطار العمل .NET.
  • السطر 16: التعليق التوضيحي WebMethod يجعل الطريقة المُعلَّقة بهذه الطريقة معروضة للعملاء البعيدين الذين سيتمكنون من استدعائها.
  • الأسطر 17-20: الطريقة HelloWorld هي طريقة توضيحية. سنقوم بحذفها لاحقًا. تتيح لنا إجراء الاختبارات الأولية واكتشاف أدوات Visual Studio بالإضافة إلى بعض العناصر التي يجب معرفتها حول خدمات الويب.
  • في [1]، نقوم بتنفيذ خدمة الويب [Service.asmx]
  • VS قام Web Developer بتشغيل خادم الويب المدمج الخاص به وجعله يستمع على منفذ عشوائي، هنا 1599. ثم تم طلب عنوان URL [2] من خادم الويب. وهو عنوان صفحة اختبار لخدمة الويب.
  • في [3]، رابط يسمح بعرض ملف وصف خدمة الويب. هذا الملف المسمى WSDL (لغة الوصف WebService) بسبب امتداده (.wsdl) هو ملف XML يصف الطرق التي تعرضها خدمة الويب. من خلال ملف WSDL هذا، يمكن للعملاء معرفة:
    • مساحة أسماء خدمة الويب
    • قائمة الطرق التي تعرضها خدمة الويب
    • المعلمات المتوقعة لكل منها
    • الاستجابة التي ترسلها كل طريقة
  • في [4]، الطريقة الوحيدة التي تعرضها خدمة الويب .
  • في [5]، محتوى الملف WSDL الذي تم الحصول عليه عبر الرابط [3]. تجدر الإشارة إلى URL [6]. من الضروري أن يكون عملاء خدمة الويب على دراية به.
  • في [7]، تسمح الصفحة التي تم الحصول عليها من خلال اتباع الرابط [4] باستدعاء طريقة [HelloWorld] لخدمة الويب
  • في [8]، النتيجة التي تم الحصول عليها: استجابة XML. تجدر الإشارة إلى URL [9] للطريقة.

تسمح دراسة الصفحات السابقة بفهم كيفية استدعاء طريقة خدمة ويب ونوع الاستجابة التي ترسلها. وهذا يسمح بكتابة عملاء HTTP قادرين على التفاعل مع خدمة الويب. تسمح معظم أدوات IDE الحالية بإنشاء عميل HTTP هذا تلقائيًا، مما يوفر على المطور عناء كتابته. وهذا هو الحال بشكل خاص مع Visual Studio Express.

قبل متابعة هذا المشروع، سنقوم بتغيير مساحة الاسم المستخدمة افتراضيًا عند إنشاء الفئات:

عندما نختار خصائص المشروع (نقرة يمنى على المشروع / خصائص)، نجد في [1] اسم المشروع l'assembly وفي [2] مساحة الاسم الافتراضية له.

وبعد ذلك،

  • في [Service1.asmx.cs]، نقوم بتغيير مساحة اسم الفئة:

using System.Web.Services;

namespace pam_v5
{
...
  public class Service1 : System.Web.Services.WebService
  {
...
  }
}
  • في [Service.asmx]، نقوم أيضًا بتغيير مساحة الاسم المستخدمة للفئة [Service1] (انقر بزر الماوس الأيمن / عرض الترميز):

<%@ WebService Language="C#" CodeBehind="Service1.asmx.cs" Class="pam_v5.Service1" %>

لنعد إلى بنية تطبيقنا:

  • الطبقة [S] هي خدمة الويب. وهي تكتفي بعرض أساليب الطبقة [metier] للعملاء البعيدين. هذه هي الطبقة التي نقوم ببنائها حالياً.
  • الطبقة [C] هي عميل HTTP لخدمة الويب. هذه هي الطبقة التي يمكن لـ IDE إنشاؤها تلقائيًا.
  • ترى الطبقة [web] الطبقة [C] كطبقة [metier] محلية إذا حرصنا على أن تقوم الطبقة [C] بتنفيذواجهة الطبقة البعيدة [metier].

نرى أدناه أن خدمة الويب الخاصة بنا ستقوم بما يلي:

  • تعرض أساليب الطبقة [metier]
  • التواصل مع هذه الأخيرة التي ستتواصل بدورها مع الطبقة [dao].

لذلك يجب أن يستخدم المشروع DLL من الطبقات [metier] و [dao]. ويتطور على النحو التالي:

  • إلى [1]، نضيف مراجع إلى المشروع
  • في [2]، يتم اختيار DLL المعتادة من المجلد [lib]. يجب التأكد من أن جميعها تحتوي على خاصية "نسخة محلية" في True. ملفات DLL المحددة هي تلك التي تنفذ الطبقات [metier] و [dao] مع دعم NHibernate.

يمكن أن يكون لتطبيق ويب من نوع "خدمة ويب ASP.NET" فئة تطبيق عامة "Global.asax" مثل تطبيق "موقع ويب ASP.NET" التقليدي. وقد رأينا فائدة مثل هذه الفئة:

  • يتم إنشاء مثيل لها عند بدء تشغيل التطبيق وتبقى في الذاكرة
  • وبالتالي يمكنها تخزين البيانات المشتركة بين جميع العملاء والتي تكون للقراءة فقط. في تطبيقنا، ستخزن هذه الفئة، كما في التطبيقات السابقة، القائمة المبسطة للموظفين. وهذا سيجنبنا البحث عن هذه القائمة في قاعدة البيانات عندما يطلبها أحد العملاء.
  • في [1]، انقر بزر الماوس الأيمن على المشروع
  • في [2]، اختر الخيار [Ajouter un nouvel élément]
  • في [3]، اختر [Classe d'application globale]
  • في [4]، تمت إضافة الملف [Global.asax] إلى المشروع

محتوى الملف [Global.asax] هو كما يلي:


<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v5.Global" Language="C#" %>

محتوى الملف [Global.asax.cs] هو كما يلي:


using System;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {

    protected void Application_Start(object sender, EventArgs e)
    {

    }
...
  }
}

ما الذي يجب أن نفعله في الطريقة Application_Start؟ بالضبط نفس الشيء الذي فعلناه في تطبيقات الويب السابقة. لنعد إلى بنية التطبيق ونضع فيها الفئة [Global]:

في المخطط أعلاه،

  • يتم إنشاء مثيل للفئة [Global] عند بدء تشغيل خدمة الويب. وتبقى في الذاكرة طالما أن خدمة الويب نشطة.
  • تقوم الفئة [Global] بإنشاء مثيلات للطبقات [metier] و [dao] في طريقتها [Application_Start]
  • لتحسين الأداء، تضع الفئة [Global] قائمة الموظفين المبسطة في حقل داخلي. وستقوم بتسليم قائمة الموظفين من هذا الحقل.
  • يتم إنشاء مثيل لخدمة الويب عند كل طلب من العميل. وتختفي بعد تلبية هذا الطلب. ولن تتواصل مباشرة مع الطبقة [metier] بل مع الفئة [Global]. وستقوم هذه الفئة بتنفيذ واجهة الطبقة [metier].

تشبه الفئة [Global] تلك التي تم إنشاؤها بالفعل للتطبيقات السابقة:


using System;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace pam_v5
{
  public class Global : System.Web.HttpApplication
  {
    // --- بيانات ثابتة للتطبيق ---
    public static Employe[] Employes;
    public static IPamMetier PamMetier = null;

    protected void Application_Start(object sender, EventArgs e)
    {
      // إنشاء مثيل الطبقة [metier]
      PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
      // استرداد الجدول المبسط للموظفين 
      Employes = PamMetier.GetAllIdentitesEmployes();
    }

    // قائمة مبسطة بالموظفين
    static public Employe[] GetAllIdentitesEmployes()
    {
      return Employes;
    }

    // راتب الموظف
    static public FeuilleSalaire GetSalaire(string SS, double heuresTravaillées, int joursTravailles)
    {
      return PamMetier.GetSalaire(SS, heuresTravaillées, joursTravailles);
    }
  }
}

تنفذ الفئة [Global] واجهة [IPamMetier] ولكن هذا غير مذكور صراحةً في الإعلان:


  public class Global : System.Web.HttpApplication, IPamMetier

في الواقع، تعد الطرق GetAllIdentitesEmployes (السطر 24) و GetSalaire (السطر 30) ثابتة، في حين أن طرق واجهة IPamMetier ليست كذلك. لذلك لا يمكن للفئة Global تنفيذ واجهة IPamMetier. بالإضافة إلى ذلك، لا يمكن إعلان الطريقتين GetAllIdentitesEmployes و GetSalaire على أنهما غير ثابتتين. في الواقع، يتم الوصول إليهما عبر اسم الفئة وليس عبر مثيل لها.

  • السطر 15: الطريقة Application_Start مشابهة لتلك الموجودة في الفئات [Global] التي تمت دراستها في الإصدارات السابقة. وهي تقوم بإنشاء مثيل للطبقة [metier] (السطر 18) ثم تهيئ (السطر 20) مصفوفة الموظفين في السطر 12.
  • السطر 24: تكتفي الطريقة GetAllIdentitesEmployes بإرجاع مصفوفة الموظفين الموجودة في السطر 12. وهنا تكمن فائدة تخزينها منذ بدء تشغيل التطبيق.
  • السطر 30: تستدعي الطريقة GetSalaire الطريقة التي تحمل الاسم نفسه في الطبقة [metier].

لإنشاء مثيل للطبقة [metier] (السطر 18)، تستخدم الفئة [Global] إطار عمل Spring. يتم تكوين هذا الإطار بواسطة الملف [Web.config] الذي يماثل الملف الخاص بالمشروع السابق: فهو يقوم بتكوين Spring و NHibernate من أجل إنشاء مثيلات للطبقات [metier] و [dao] الخاصة بخدمة الويب.

لنعد إلى بنية تطبيق العميل/الخادم الخاص بنا:

على جانب الخادم، لم يتبق سوى كتابة خدمة الويب [S] نفسها. إذا عدنا إلى بنية التطبيق:

نرى أنه من جانب الخادم، جميع الطبقات التي تسبق الطبقة [metier] تنفذ واجهة هذه الطبقة IPamMetier. هذا ليس إلزاميًا ولكنه نهج يبدو منطقيًا. يمكن تطبيق هذا المنطق على جانب العميل، على العميل [C] لخدمة الويب [S]. وبالتالي، فإن جميع الطبقات التي تفصل الطبقة [web] عن الطبقة [metier] تنفذ واجهة IPamMetier. وبذلك يمكن القول إننا عدنا إلى تطبيق ثلاثي الطبقات:

  • طبقة العرض [web] [1]
  • الطبقة [metier] [2]
  • طبقة الوصول إلى البيانات [3]

قد يكون تنفيذ خدمة الويب [Service1.asmx.cs] كما يلي:


using System.Web.Services;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;

namespace pam_v5
{
  [WebService(Namespace = "http://st.istia.univ-angers.fr/")]
  [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
  [System.ComponentModel.ToolboxItem(false)]
  public class Service1 : System.Web.Services.WebService, IPamMetier
  {

    // قائمة بجميع هويات الموظفين 
    [WebMethod]
    public Employe[] GetAllIdentitesEmployes()
    {
      return Global.GetAllIdentitesEmployes();
    }

    // ------- حساب الراتب 
    [WebMethod]
    public FeuilleSalaire GetSalaire(string ss, double heuresTravaillees, int joursTravailles)
    {
      return Global.GetSalaire(ss, heuresTravaillees, joursTravailles);
    }
  }
}
  • السطر 8: يتم توضيح الفئة بالسمة [WebService] ونقوم بتسمية مساحة أسماء خدمة الويب
  • السطر 11: ترث الفئة [Service1] الفئة [WebService] وتنفذ الواجهة [IPamMetier]
  • السطران 15 و22: يتم توضيح كل طريقة في الفئة بالسمة [WebMethod] لكي يتم عرضها للعملاء البعيدين. بشكل افتراضي، يتم عرض جميع الطرق العامة لخدمة الويب. وبالتالي، فإن السمات الموجودة في السطرين 15 و22 اختيارية هنا. لتنفيذ واجهة [IPamMetier]، تكتفي كل طريقة باستدعاء الطريقة التي تحمل الاسم نفسه في الفئة [Global].

نحن جاهزون لتنفيذ خدمة الويب:

  • في [1]، يتم إعادة إنشاء المشروع
  • في [2]، يتم تحديد خدمة الويب [Service1.asmx] وعرضها في المتصفح [3]
  • في [4]، الصفحة الويب المعروضة. وهي تعرض طرق خدمة الويب.
  • في [4]، نتبع الرابط [GetAllIdentitesEmployes] ونحصل في [5] على صفحة اختبار هذه الطريقة.
  • في [6]، نجد URL الخاص بالطريقة
  • في [7]، الزر [Appeler] الذي يسمح باختبار الطريقة. لا تتطلب هذه الطريقة أي معلمات.
  • في [8]، النتيجة XML التي ترسلها خدمة الويب. وفيها، تعتبر الخصائص SS و Nom، Prenom من كائنات Employe ذات أهمية لأن الطريقة [GetAllIdentitesEmployes] لا تطلب سوى هذه الخصائص. ومع ذلك، فإن هذه الطريقة تُرجع مصفوفة من كائنات Employe. ونلاحظ في [8] أن الخصائص الرقمية Id و Version موجودة في تدفق Xml المرتجع، ولكن الخصائص التي لها قيمة null غير موجودة: Adresse، Ville، CodePostal، Indemnites.

لدينا خدمة ويب نشطة. سنقوم الآن بكتابة عميل C# لها. للقيام بذلك، سنحتاج إلى URI من ملف WSDL الخاص بخدمة الويب. نحصل عليه من الصفحة التي يتم عرضها مبدئيًا عند تشغيل [Service.asmx]:

  • في [1]، URI من خدمة الويب
  • إلى [2]، والرابط الذي يؤدي إلى ملفه WSDL
  • في [3]، قيمة هذا الرابط

9.3. مشروع C# لعميل خدمة الويب NUnit

نقوم بإنشاء مشروع C# (باستخدام Visual C# وليس Visual Web Developer) لعميل خدمة الويب. سيكون هذا عميل اختبار NUnit. كما سيكون المشروع من نوع "مكتبة الفئات".

  • في [1]، نقوم بإنشاء مشروع C# من نوع "مكتبة الفئات"
  • في [2]، نسمي المشروع
  • في [3]، المشروع. نحذف [Class1.cs].
  • في [4]، المشروع الجديد.
  • في خصائص المشروع، في علامة التبويب [Application] [5]، نحدد مساحة أسماء المشروع. كل فئة يتم إنشاؤها بواسطة IDE ستكون في هذه المساحة.

نحفظ مشروعنا الجديد في المكان الذي يناسبنا:

 

بعد ذلك، نقوم بإنشاء عميل خدمة الويب البعيدة. لفهم ما سنقوم به، يجب العودة إلى بنية العميل/الخادم قيد الإنشاء:

سيقوم IDE بإنشاء طبقة العميل [C] من URI في ملف WSDL لخدمة الويب [S]. تجدر الإشارة إلى أن URI لهذا الملف قد تم تسجيله مسبقًا. نقوم بإجراء " " بالطريقة التالية:

  • إلى [1]، انقر بزر الماوس الأيمن على الفرع References وأضف مرجع خدمة
  • في [2]، حدد URL لملف WSDL لخدمة الويب التي تم تدوينها سابقًا. يجب تشغيل هذه الخدمة مسبقًا إذا لم تكن قيد التشغيل.
  • في [3]، اطلب اكتشاف خدمة الويب من خلال ملفها WSDL
  • في [4]، خدمة الويب التي تم اكتشافها
  • في [5]، الطرق المعروضة بواسطة خدمة الويب.
  • في [6]، مساحة الأسماء التي نريد وضع فئات وواجهات العميل التي سيتم إنشاؤها فيها.
  • نقوم بتأكيد المساعد

  • في [1]، العميل الذي تم إنشاؤه. نضغط عليه مرتين للوصول إلى محتواه.
  • في [2]، في مستكشف الكائنات، يتم عرض الفئات والواجهات الخاصة بمساحة الاسم Client.WsPam. هذه هي مساحة الاسم للعميل الذي تم إنشاؤه.
  • في [3]، الفئة التي تنفذ عميل خدمة الويب.
  • في [4]، الطرق التي ينفذها العميل [Service1SoapClient]. نجد هنا طريقتي خدمة الويب البعيدة [5] و [6].
  • في [2]، نجد صور كيانات الطبقات:
    • [metier]: FeuilleSalaire، ElementsSalaire
    • [dao]: Employe، Cotisations، Indemnites

فيما يلي، يجب تذكر أن صور الكيانات البعيدة هذه موجودة على جانب العميل وفي مساحة الاسم PamV5Client.WsPam.

دعونا نلقي نظرة على الطرق والخصائص التي تعرضها إحداها:

  • في [1]، يتم تحديد الفئة المحلية [Employe]
  • في [2]، نجد خصائص الكيان البعيد [Employe] بالإضافة إلى الحقول الخاصة المستخدمة لاحتياجات الكيان المحلي الخاصة.

لنعد إلى تطبيق C# الخاص بنا. نضيف إليه فئة اختبار NUnit:

  • في [1]، تمت إضافة الفئة [NUnit]. ستحتاج الفئة [NUnit] إلى إطار العمل NUnit وبالتالي إلى مرجع إلى DLL الخاص به. نفترض هنا أن إطار العمل NUnit قد تم تثبيته على الجهاز (http://nunit.org/).
  • في [2]، نضيف مرجعًا إلى المشروع
  • في علامة التبويب [3] .NET التي تجمع ملفات DLL المسجلة على الجهاز، نختار [4]، و DLL و [nunit.framework] الإصدار 2.4.6 كحد أدنى.

بالإضافة إلى ذلك، سنستخدم Spring لإنشاء مثيل للعميل المحلي [C] لخدمة الويب [S]:

يمكن إضافة مرجع Spring DLL كما تم مع إطار العمل NUnit إذا تم تسجيل DLL مسبقًا على الجهاز (http://www.springframework.net/download.html).

نحن نتبع طريقة مختلفة. نستخدم المجلد [lib] من المشاريع السابقة الذي كان يحتوي على ملفات DLL اللازمة لـ Spring ونضيف مرجع Spring إلى المشروع:

لنعد إلى بنية العميل قيد الإنشاء:

فيما سبق، نرى أن عميل الاختبار [1] يتفاعل مع طبقة [metier] [2] الموسعة. وتقدم هذه الطبقة نفس الأساليب التي تقدمها الطبقة البعيدة [metier]. لذلك يمكننا استخدام فئة الاختبار التي سبق أن صادفناها أثناء اختبار الطبقة [metier] في مشروع C# [pam-metier-dao-nhibernate]:


using NUnit.Framework;
using Pam.Dao.Entites;
using Pam.Metier.Entites;
using Pam.Metier.Service;
using Spring.Context.Support;

namespace Pam.Metier.Tests {

    [TestFixture()]
    public class NunitTestPamMetier : AssertionHelper {

        // الطبقة [metier] المراد اختبارها 
        private IPamMetier pamMetier;

        // المنشئ
        public NunitTestPamMetier() {
            // إنشاء مثيل الطبقة [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }


        [Test]
        public void GetAllIdentitesEmployes() {
            // التحقق من عدد الموظفين 
            Expect(2, EqualTo(pamMetier.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetSalaire1() {
            // حساب كشف الراتب 
            FeuilleSalaire feuilleSalaire = pamMetier.GetSalaire("254104940426058", 150, 20);
            // التحقق 
            Expect(368.77, EqualTo(feuilleSalaire.ElementsSalaire.SalaireNet).Within(1E-06));
            // كشوف رواتب موظف غير موجود 
            bool erreur = false;
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

    }
}

هناك بعض التعديلات التي يجب إجراؤها:

  • في السطر 18، يتم إنشاء مثيل للطبقة [metier] باستخدام إطار عمل Spring. الفئة ليست هي نفسها في الحالتين. هنا، الطبقة المحلية [metier] هي مثيل للفئة [PamV5Client.WsPam.Service1SoapClient]، وهي الفئة التي تم إنشاؤها بواسطة IDE. لذلك تم تكوين Spring بالطريقة التالية في ملف [app.config] لمشروع C#:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>

    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object id="pammetier" type="PamV5Client.WsPam.Service1SoapClient, pam-v5-client-csharp-webservice"/>
        </objects>
    </spring>


    <system.serviceModel>
...
  • السطر 16 أعلاه، الكائن [pammetier] هو مثيل للفئة [PamV5Client.WsPam.Service1SoapClient] الموجودة في التجميع (assembly) [pam-v5-client-csharp-webservice]. للحصول على المعلومة الأولى، يكفي الرجوع إلى تعريف الفئة [Service1SoapClient] في مستكشف الكائنات (الفقرة 9.3):
  • في [2]، فئة تنفيذ الطبقة المحلية [metier] وفي [1] مساحة أسمائها
  • إلى [3]، وفي خصائص المشروع، اسم التجميع، والمعلومة الثانية اللازمة لتكوين كائن Spring [pammetier].

لنعد إلى كود إنشاء مثيل الطبقة المحلية [metier] في [NUnit.cs]:


        // الطبقة [metier] المطلوب اختبارها 
        private IPamMetier pamMetier;

        // المنشئ
        public NunitTestPamMetier() {
            // تجسيد الطبقة [dao]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as IPamMetier;
        }

السطر 7، كانت الطبقة البعيدة [metier] من النوع IPamMetier. هنا، الطبقة [metier] هي من النوع [Service1SoapClient]:


public class Service1SoapClient : System.ServiceModel.ClientBase<Service1Soap>

نلاحظ أن الفئة Service1SoapClient لا تنفذ واجهة IPamMetier حتى وإن كانت تعرض طرقًا تحمل نفس الاسم. لذا، يتعين علينا كتابة مثيل الطبقة المحلية [metier] بالطريقة التالية:


        // الطبقة [metier] المراد اختبارها 
        private Service1SoapClient pamMetier;

        // المنشئ
        public NunitTestPamMetier() {
            // إنشاء مثيل الطبقة [metier]
            pamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
}

تعديل آخر يجب إجراؤه:


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (PamException) {
                erreur = true;
            }
            Expect(erreur, True);
        }

يستخدم الكود أعلاه، في السطر 6، النوع PamException الذي لا يوجد على جانب العميل. سنستبدله بفئته الأصلية، النوع Exception.


        [Test]
        public void GetSalaire1() {
...
            try {
                feuilleSalaire = pamMetier.GetSalaire("xx", 150, 20);
            } catch (Exception) {
                erreur = true;
            }
            Expect(erreur, True);
        }

أخيرًا، لم تعد مساحات الأسماء المستوردة هي نفسها:


using System;
using PamV5Client.WsPam;
using NUnit.Framework;
using Spring.Context.Support;

وبعد ذلك، يمكن إنشاء مشروع من نوع "مكتبة الفئات". يتم إنشاء DLL التالي:

  • في [1]، المجلد [bin/Release] لمشروع C#
  • في [2]، DLL للمشروع.

ثم يتم تنفيذ الاختبار NUnit بواسطة إطار العمل NUnit (يجب أن تكون قاعدة MySQL dbpam_nhibernate نشطة للاختبار):

  • في [3] و [4]، يتم تحميل DLL [2] في تطبيق الاختبار NUnit
  • في [5]، يتم تحديد فئة الاختبار وتنفيذها [6]
  • في [7]، نتائج الاختبار الناجح

لدينا الآن خدمة ويب جاهزة للعمل.