2. تكامل Spring/NHibernate
يوفر إطار عمل Spring فئات أدوات مساعدة للعمل مع إطار عمل NHibernate. يسهل استخدام هذه الفئات كتابة التعليمات البرمجية للوصول إلى البيانات من نظام إدارة قواعد البيانات. انظر إلى البنية متعددة الطبقات التالية:
![]() |
في الأقسام التالية، سنقوم ببناء طبقة [DAO] باستخدام [Spring / NHibernate] من خلال التعليق على كود حل عملي. لن نحاول تغطية جميع التكوينات أو الاستخدامات الممكنة لإطار عمل [Spring / NHibernate]. يمكن للقراء تكييف الحل المقترح مع احتياجاتهم الخاصة بمساعدة وثائق Spring.NET [Spring.NET | الصفحة الرئيسية ] (ديسمبر 2011).
2.1. طبقة الوصول إلى البيانات [DAO]
![]() |
قاعدة البيانات هي قاعدة بيانات MySQL [dbpam_nhibernate] التي تم عرضها بالفعل في القسم 1.2. تنفذ طبقة [DAO] واجهة C# التالية:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// list of all employee identities
Employe[] GetAllIdentitesEmployes();
// an individual employee with benefits
Employe GetEmploye(string ss);
// list of all contributions
Cotisations GetCotisations();
}
}
2.1.1. مشروع Visual Studio C# لطبقة [DAO]
مشروع Visual Studio لطبقة [dao] هو كما يلي:
![]() |
- في [1]، المشروع ككل
- يحتوي المجلد [pam] على فئات المشروع بالإضافة إلى تكوين كيان NHibernate
- يقوم ملفا [App.config] و[Dao.xml] بتكوين إطار عمل Spring/NHibernate. سنحتاج إلى وصف محتويات هذين الملفين.
- في [2]، الفئات المختلفة للمشروع
- في المجلد [entities]، نجد كيانات NHibernate التي تمت دراستها في المشروع السابق (انظر الصفحة 14)
- في المجلد [service]، نجد واجهة [IPamDao] وتنفيذها باستخدام إطار عمل Spring/NHibernate [PamDaoSpringNHibernate].
- يحتوي المجلد [tests] على اختبارات واجهة [IPamDao].
- في [3]، مراجع المشروع. يتطلب تكامل Spring/NHibernate مكتبات DLL جديدة [4].
في مراجع المشروع [3]، نجد مكتبات DLL التالية:
- NHibernate: لـ NHibernate ORM
- MySql.Data: برنامج تشغيل ADO.NET لنظام إدارة قواعد البيانات MySQL 5
- Spring.Core: لإطار عمل Spring، الذي يتولى تكامل الطبقات
- log4net: مكتبة تسجيل
- nunit.framework: مكتبة اختبار الوحدات
- Spring.Aop و Spring.Data و Spring.Data.NHibernate32: توفر دعم Spring/NHibernate.
سنحرص على ضبط خاصية "Copy to Local" لجميع ملفات DLL هذه على True.
2.1.2. تكوين مشروع C#
يتكون المشروع على النحو التالي:
![]() |
- في [1]، اسم تجميع المشروع هو [pam-dao-spring-nhibernate]. يظهر هذا الاسم في ملفات تكوين المشروع المختلفة.
2.1.3. الكيانات في طبقة [dao]
![]() |
تم تجميع الكيانات (الكائنات) المطلوبة لطبقة [dao] في مجلد [entities] [1] الخاص بالمشروع. هذه الكيانات هي نفسها الموجودة في المشروع السابق (انظر القسم 1.3.2)، مع اختلاف واحد في ملفات تكوين NHibernate. خذ على سبيل المثال ملف [Employee.hbm.xml]:
- في [2]، تم تكوين الملف ليتم تضمينه في تجميع المشروع
محتواه كما يلي:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
namespace="Pam.Dao.Entites" assembly="pam-dao-spring-nhibernate">
<class name="Employe" table="EMPLOYES">
<id name="Id" column="ID">
<generator class="native" />
</id>
<version name="Version" column="VERSION"/>
<property name="SS" column="SS" length="15" not-null="true" unique="true"/>
<property name="Nom" column="NOM" length="30" not-null="true"/>
<property name="Prenom" column="PRENOM" length="20" not-null="true"/>
<property name="Adresse" column="ADRESSE" length="50" not-null="true" />
<property name="Ville" column="VILLE" length="30" not-null="true"/>
<property name="CodePostal" column="CP" length="5" not-null="true"/>
<many-to-one name="Indemnites" column="INDEMNITE_ID" cascade="all" lazy="false"/>
</class>
</hibernate-mapping>
- السطر 3: تشير سمة التجميع إلى أن الملف [Employe.hbm.xml] موجود في التجميع [pam-dao-spring-nhibernate]
بالإضافة إلى ذلك، في المجلد [entities]، نجد فئة استثناء يستخدمها المشروع:
using System;
namespace Pam.Dao.Entites {
public class PamException : Exception {
// the error code
public int Code { get; set; }
// manufacturers
public PamException() {
}
public PamException(int Code)
: base() {
this.Code = Code;
}
public PamException(string message, int Code)
: base(message) {
this.Code = Code;
}
public PamException(string message, Exception ex, int Code)
: base(message, ex) {
this.Code = Code;
}
}
}
تم اشتقاق فئة [PamException] من فئة [Exception] (السطر 4) لإضافة رمز خطأ (السطر 7).
2.1.4. تكوين Spring / NHibernate
لنعد إلى مشروع Visual C#:
![]() |
- في [1]، يقوم ملفا [App.config] و[Dao.xml] بتكوين تكامل Spring / NHibernate
2.1.4.1. ملف [ App.config]
ملف [App.config] هو كما يلي:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<!-- configuration sections -->
<configSections>
<sectionGroup name="spring">
<section name="parsers" type="Spring.Context.Support.NamespaceParsersSectionHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
</sectionGroup>
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
</configSections>
<!-- spring configuration -->
<spring>
<parsers>
<parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
</parsers>
<context>
<resource uri="Dao.xml" />
</context>
</spring>
<!-- This section contains the log4net configuration settings -->
<!-- NOTE IMPORTANTE: logs are not active by default. They must be activated by program
avec l'instruction log4net.Config.XmlConfigurator.Configure();
! -->
<log4net>
<appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="%-5level %logger - %message%newline" />
</layout>
</appender>
<!-- Set default logging level to DEBUG -->
<root>
<level value="DEBUG" />
<appender-ref ref="ConsoleAppender" />
</root>
<!-- Set logging for Spring . Logger names in Spring correspond to the namespace -->
<logger name="Spring">
<level value="INFO" />
</logger>
<logger name="Spring.Data">
<level value="DEBUG" />
</logger>
<logger name="NHibernate">
<level value="DEBUG" />
</logger>
</log4net>
</configuration>
يقوم ملف [App.config] أعلاه بتكوين Spring (الأسطر 5–9، 15–22)، وlog4net (السطر 10، الأسطر 28–53)، ولكنه لا يقوم بتكوين NHibernate. لا يتم تكوين كائنات Spring في ملف [App.config] بل في ملف [Dao.xml] (السطر 20). وبالتالي، فإن تكوين Spring/NHibernate، الذي يتكون من إعلان كائنات Spring محددة، سيوجد في هذا الملف.
2.1.4.2. ملف [Dao.xml]
ملف [Dao.xml]، الذي يحتوي على الكائنات التي يديرها Spring، هو كما يلي:
<?xml version="1.0" encoding="utf-8" ?>
<objects xmlns="http://www.springframework.net"
xmlns:db="http://www.springframework.net/database">
<!-- Referenced by main application context configuration file -->
<description>
Application Spring / NHibernate
</description>
<!-- Database and NHibernate Configuration -->
<db:provider id="DbProvider"
provider="MySql.Data.MySqlClient"
connectionString="Server=localhost;Database=dbpam_nhibernate;Uid=root;Pwd=;"/>
<object id="NHibernateSessionFactory" type="Spring.Data.NHibernate.LocalSessionFactoryObject, Spring.Data.NHibernate32">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>pam-dao-spring-nhibernate</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="dialect" value="NHibernate.Dialect.MySQL5Dialect"/>
<entry key="hibernate.show_sql" value="false"/>
</dictionary>
</property>
<property name="ExposeTransactionAwareSessionFactory" value="true" />
</object>
<!-- transaction manager -->
<object id="transactionManager"
type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
<property name="DbProvider" ref="DbProvider"/>
<property name="SessionFactory" ref="NHibernateSessionFactory"/>
</object>
<!-- Hibernate Template -->
<object id="HibernateTemplate" type="Spring.Data.NHibernate.Generic.HibernateTemplate">
<property name="SessionFactory" ref="NHibernateSessionFactory" />
<property name="TemplateFlushMode" value="Auto" />
<property name="CacheQueries" value="true" />
</object>
<!-- Data Access Objects -->
<object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
</objects>
- تقوم الأسطر 11–13 بتكوين الاتصال بقاعدة البيانات [dbpam_nhibernate]. وهي تتضمن:
- موفر ADO.NET المطلوب للاتصال، وهو في هذه الحالة موفر نظام إدارة قواعد البيانات MySQL. وهذا يتطلب تضمين مكتبة DLL [Mysql.Data] في مراجع المشروع.
- سلسلة اتصال قاعدة البيانات (الخادم، اسم قاعدة البيانات، مالك الاتصال، وكلمة المرور)
- تقوم الأسطر 15–29 بتكوين NHibernate SessionFactory، وهو الكائن المستخدم للحصول على جلسات NHibernate. لاحظ أن جميع عمليات قاعدة البيانات يتم تنفيذها ضمن جلسة NHibernate. في السطر 15، يمكننا أن نرى أن SessionFactory يتم تنفيذه بواسطة فئة Spring Spring.Data.NHibernate.LocalSessionFactoryObject، الموجودة في مكتبة DLL Spring.Data.NHibernate32.
- السطر 16: تحدد الخاصية DbProvider معلمات اتصال قاعدة البيانات (موفر ADO.NET وسلسلة الاتصال). هنا، تشير هذه الخاصية إلى كائن DbProvider المحدد سابقًا في الأسطر 11-13.
- الأسطر 17-20: حدد قائمة التجميعات التي تحتوي على ملفات [*.hbm.xml] التي تكوّن الكيانات التي يديرها NHibernate. تشير السطر 19 إلى أن هذه الملفات ستوجد في تجميع المشروع. لاحظ أن هذا الاسم موجود في خصائص مشروع C#. لاحظ أيضًا أن جميع ملفات [*.hbm.xml] قد تم تكوينها ليتم تضمينها في تجميع المشروع.
- الأسطر 22-27: خصائص خاصة بـ NHibernate.
- السطر 24: ستكون لهجة SQL المستخدمة هي MySQL
- السطر 25: لن يظهر SQL الذي تم إنشاؤه بواسطة NHibernate في سجلات وحدة التحكم. يتيح لك تعيين هذه الخاصية على true رؤية عبارات SQL التي تم إنشاؤها بواسطة NHibernate. يمكن أن يساعدك هذا في فهم، على سبيل المثال، سبب بطء التطبيق عند الوصول إلى قاعدة البيانات.
- السطر 28: سيؤدي تعيين الخاصية ExposeTransactionAwareSessionFactory إلى true إلى قيام Spring بإدارة تعليقات إدارة المعاملات الموجودة في كود C#. سنعود إلى هذا عندما نكتب الفئة التي تنفذ طبقة [DAO].
- تحدد الأسطر 32–36 مدير المعاملات. مرة أخرى، هذا المدير هو فئة Spring من مكتبة DLL Spring.Data.NHibernate32. يتطلب هذا المدير معلمات اتصال قاعدة البيانات (السطر 34) بالإضافة إلى NHibernate SessionFactory (السطر 35).
- تحدد الأسطر 39-43 خصائص فئة HibernateTemplate، وهي أيضًا فئة Spring. ستُستخدم هذه الفئة كفئة مساعدة في الفئة التي تنفذ طبقة [DAO]. وهي تسهل التفاعلات مع كائنات NHibernate. تحتوي هذه الفئة على خصائص معينة يجب تهيئتها:
- السطر 40: كائن SessionFactory الخاص بـ NHibernate
- السطر 41: تحدد الخاصية TemplateFlushMode وضع المزامنة لسياق الاستمرارية NHibernate مع قاعدة البيانات. يضمن الوضع التلقائي (Auto) حدوث المزامنة:
- في نهاية المعاملة
- قبل عملية SELECT
- السطر 42: سيتم تخزين استعلامات HQL (لغة استعلام Hibernate) مؤقتًا. وهذا قد يؤدي إلى تحسين الأداء.
- تحدد الأسطر 46-48 فئة التنفيذ لطبقة [dao]
- السطر 46: سيتم تنفيذ طبقة [dao] بواسطة فئة [PamdaoSpringNHibernate] في مكتبة DLL [pam-dao-spring-nhibernate]. بعد إنشاء مثيل للفئة، سيتم تنفيذ طريقة init الخاصة بالفئة على الفور. عند إغلاق حاوية Spring، سيتم تنفيذ طريقة destroy الخاصة بالفئة.
- السطر 47: ستحتوي فئة [PamDaoSpringNHibernate] على خاصية HibernateTemplate التي سيتم تهيئتها باستخدام خاصية HibernateTemplate من السطر 39.
2.1.5. تنفيذ طبقة [dao]
2.1.5.1. هيكل فئة التنفيذ
واجهة [IPamDao] هي كما يلي:
using Pam.Dao.Entites;
namespace Pam.Dao.Service {
public interface IPamDao {
// list of all employee identities
Employe[] GetAllIdentitesEmployes();
// an individual employee with benefits
Employe GetEmploye(string ss);
// list of all contributions
Cotisations GetCotisations();
}
}
- السطر 1: نقوم باستيراد مساحة الاسم للكيانات في طبقة [dao].
- السطر 3: تقع طبقة [dao] في مساحة اسم [Pam.Dao.Service]. يمكن إنشاء العناصر الموجودة في مساحة اسم [Pam.Dao.Entities] في مثيلات متعددة. يتم إنشاء العناصر الموجودة في مساحة اسم [Pam.Dao.Service] كمثيل واحد (singleton). وهذا ما برر اختيار أسماء مساحات الأسماء.
- السطر 4: تسمى الواجهة [IPamDao]. وهي تحدد ثلاث طرق:
- السطر 6: تُرجع [GetAllIdentitesEmployes] مصفوفة من الكائنات من النوع [Employe] تمثل قائمة مقدمي رعاية الأطفال بتنسيق مبسط (اللقب، الاسم الأول، رقم الضمان الاجتماعي).
- السطر 8: تُرجع [GetEmploye] كائن [Employe]: الموظف الذي يحمل رقم الضمان الاجتماعي الذي تم تمريره كمعلمة إلى الطريقة، إلى جانب المزايا المرتبطة بدرجة راتبه.
- السطر 10، [GetCotisations] تُرجع كائن [Cotisations]، الذي يغلف معدلات الاشتراكات المختلفة في الضمان الاجتماعي التي سيتم خصمها من الراتب الإجمالي.
قد يكون الهيكل الأساسي لفئة التنفيذ لهذه الواجهة باستخدام Spring/NHibernate كما يلي:
using System;
using System.Collections;
using System.Collections.Generic;
using Pam.Dao.Entites;
using Spring.Data.NHibernate.Generic.Support;
using Spring.Transaction.Interceptor;
namespace Pam.Dao.Service {
public class PamDaoSpringNHibernate : HibernateDaoSupport, IPamDao {
// private fields
private Cotisations cotisations;
private Employe[] employes;
// init
[Transaction(ReadOnly = true)]
public void init() {
...
}
// delete object
public void destroy() {
if (HibernateTemplate.SessionFactory != null) {
HibernateTemplate.SessionFactory.Close();
}
}
// list of all employee identities
public Employe[] GetAllIdentitesEmployes() {
return employes;
}
// an individual employee with benefits
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
....
}
// list of contributions
public Cotisations GetCotisations() {
return cotisations;
}
}
}
- السطر 9: تنفذ فئة [PamDaoSpringNHibernate] واجهة طبقة [dao] [IPamDao] بشكل صحيح. كما أنها مشتقة من فئة Spring [HibernateDaoSupport]. تحتوي هذه الفئة على خاصية [HibernateTemplate] يتم تهيئتها بواسطة تكوين Spring الذي تم إعداده (السطر 2 أدناه):
<object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
<property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
- في السطر 1 أعلاه، نرى أن تعريف الكائن [pamdao] يحدد أن طريقتي init و destroy للفئة [PamDaoSpringNHibernate] يجب أن يتم تنفيذهما في أوقات محددة. وهاتان الطريقتان موجودتان بالفعل في الفئة في السطرين 16 و 21.
- السطران 15 و 34: تعليقات توضيحية تضمن تنفيذ الطريقة المُعلَّمة ضمن معاملة. تشير السمة ReadOnly=true إلى أن المعاملة للقراءة فقط. قد تُحدث الطريقة المنفَّذة ضمن المعاملة استثناءً. في هذه الحالة، يقوم Spring تلقائيًا بإلغاء المعاملة. تزيل هذه التعليقات التوضيحية الحاجة إلى إدارة معاملة داخل الطريقة.
- السطر 16: يتم تنفيذ الطريقة init بواسطة Spring فور إنشاء مثيل للفئة. سنرى أن الغرض منها هو تهيئة الحقول الخاصة في السطرين 11 و 12. وسيتم تشغيلها ضمن معاملة (السطر 15).
- يتم تنفيذ طرق واجهة [IPamDao] في الأسطر 28 و35 و40.
- الأسطر 28-30: تعرض طريقة [GetAllIdentitesEmployes] ببساطة السمة من السطر 12، والتي تم تهيئتها بواسطة طريقة init.
- الأسطر 40-42: تعيد طريقة [GetCotisations] ببساطة السمة من السطر 11، والتي تم تهيئتها بواسطة طريقة init.
2.1.5.2. طرق مفيدة لفئة HibernateTemplate
سنستخدم الطرق التالية لفئة HibernateTemplate:
تنفيذ استعلام HQL وإرجاع قائمة من الكائنات من النوع T | |
يُنفذ استعلام HQL معلمات بواسطة ?. يتم توفير قيم هذه المعلمات بواسطة مصفوفة الكائنات. | |
تُرجع جميع الكيانات من النوع T |
هناك طرق أخرى مفيدة لن تتاح لنا الفرصة لاستخدامها، والتي تتيح لك استرداد الكيانات وحفظها وتحديثها وحذفها:
يضيف الكيان من النوع T ذي المفتاح الأساسي id إلى جلسة NHibernate. | |
يقوم بإدراج (INSERT) أو تحديث (UPDATE) كائن الكيان اعتمادًا على ما إذا كان يحتوي على مفتاح أساسي (UPDATE) أم لا (INSERT). يمكن تكوين عدم وجود مفتاح أساسي باستخدام السمة `unsaved-values` في ملف تكوين الكيان. بعد عملية `SaveOrUpdate`، يكون كائن الكيان موجودًا في جلسة NHibernate. | |
يزيل كائن الكيان من جلسة NHibernate. |
2.1.5.3. تنفيذ طريقة init
طريقة init لفئة [PamDaoSpringNHibernate] هي، حسب التكوين، الطريقة التي يتم تنفيذها بعد أن يقوم Spring بإنشاء مثيل للفئة. والغرض منها هو تخزين هويات الموظفين المبسطة (اللقب، الاسم الأول، رقم الضمان الاجتماعي) ومعدلات الاشتراكات محليًا. ويمكن أن يكون كودها كما يلي.
[Transaction(ReadOnly = true)]
public void init() {
try {
// retrieve the simplified list of employees
IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
// we put it in a table
employes = new Employe[lignes.Count];
int i = 0;
foreach (object[] ligne in lignes) {
employes[i] = new Employe() { SS = ligne[0].ToString(), Nom = ligne[1].ToString(), Prenom = ligne[2].ToString() };
i++;
}
// we put the contribution rates in a
cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
} catch (Exception ex) {
// on transforme l'exception
throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
}
}
- السطر 5: يتم تنفيذ استعلام HQL. يسترد الحقول SS و LastName و FirstName من جميع كيانات Employee. ويعيد قائمة من الكائنات. لو كنا قد طلبنا سجل Employee بالكامل باستخدام "select e from Employee e"، لكنا قد استردنا قائمة من الكائنات من النوع Employee.
- الأسطر 7–12: يتم نسخ قائمة الكائنات هذه إلى مصفوفة من الكائنات من نوع Employee.
- السطر 14: نسترد قائمة بجميع الكيانات من النوع Contributions. ونعلم أن هذه القائمة تحتوي على عنصر واحد فقط. لذلك نسترد العنصر الأول من القائمة للحصول على معدلات المساهمة.
- تقوم السطران 7 و 14 بتهيئة الحقلين الخاصين للفئة.
2.1.5.4. تنفيذ طريقة GetEmployee
يجب أن تُرجع طريقة GetEmployee كيان Employee برقم ضمان اجتماعي معين. قد يكون كودها كما يلي:
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
IList<Employe> employés = null;
try {
// request
employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
} catch (Exception ex) {
// on transforme l'exception
throw new PamException(string.Format("Erreur d'accès à la BD lors de la demande de l'employé de n° ss [{0}] : [{1}]", ss, ex.ToString()), 41);
}
// has an employee been recovered?
if (employés.Count == 0) {
// we report the fact
throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
} else {
return employés[0];
}
}
- السطر 6: يسترد قائمة الموظفين الذين يحملون رقم ضمان اجتماعي معين
- السطر 12: عادةً، إذا كان الموظف موجودًا، يجب أن نحصل على قائمة تحتوي على عنصر واحد
- السطر 14: إذا لم يكن الأمر كذلك، فقم بإطلاق استثناء
- السطر 16: إذا كان الأمر كذلك، فقم بإرجاع أول موظف في القائمة
2.2. اختبارات طبقة [DAO]
2.2.1. مشروع Visual Studio
تم عرض مشروع Visual Studio بالفعل. للتذكير:
![]() |
- في [1]، المشروع ككل
- في [2]، الفئات المختلفة للمشروع. يحتوي المجلد [tests] على اختبار وحدة التحكم [Main.cs] واختبار الوحدة [NUnit.cs].
- في [3]، يتم ترجمة البرنامج [Main.cs].
![]() |
- في [4]، لم يتم إنشاء الملف [NUnit.cs].
- المشروع عبارة عن تطبيق وحدة تحكم. الفئة التي يتم تنفيذها هي تلك المحددة في [5]، وهي الفئة الموجودة في ملف [Main.cs].
2.2.2. برنامج الاختبار الخاص بوحدة التحكم [Main.cs]
يعمل برنامج الاختبار [Main.cs] في البنية التالية:
![]() |
وهو مسؤول عن اختبار أساليب واجهة [IPamDao]. ومن الأمثلة الأساسية على ذلك ما يلي:
using System;
using Pam.Dao.Entites;
using Pam.Dao.Service;
using Spring.Context.Support;
namespace Pam.Dao.Tests {
public class MainPamDaoTests {
public static void Main() {
try {
// layer instantiation [dao]
IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
// list of employee identities
foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
Console.WriteLine(Employe.ToString());
}
// an employee with benefits
Console.WriteLine("------------------------------------");
Console.WriteLine(pamDao.GetEmploye("254104940426058"));
Console.WriteLine("------------------------------------");
// list of contributions
Cotisations cotisations = pamDao.GetCotisations();
Console.WriteLine(cotisations.ToString());
} catch (Exception ex) {
// exception display
Console.WriteLine(ex.ToString());
}
//break
Console.ReadLine();
}
}
}
- السطر 11: نطلب من Spring مرجعًا إلى طبقة [dao].
- الأسطر 13–15: اختبار طريقة [GetAllEmployeeIDs] لواجهة [IPamDao]
- السطر 18: اختبار طريقة [GetEmploye] لواجهة [IPamDao]
- السطر 21: اختبار طريقة [GetCotisations] لواجهة [IPamDao]
يتم تكوين Spring وNHibernate وlog4net عبر ملف [ App.config] الذي تمت مناقشته في القسم 2.1.4.1.
يؤدي تشغيل البرنامج باستخدام قاعدة البيانات الموضحة في القسم 1.2 إلى إخراج وحدة التحكم التالي:
- السطران 1-2: الموظفان من النوع [Employee] مع المعلومات [SS، Last Name، First Name] فقط
- السطر 4: الموظف من النوع [Employee] الذي يحمل رقم الضمان الاجتماعي [254104940426058]
- السطر 5: معدلات الاشتراكات
2.2.3. اختبارات الوحدة باستخدام NUnit
سننتقل الآن إلى اختبار وحدة NUnit. سيتطور مشروع Visual Studio لطبقة [dao] على النحو التالي:
![]() |
- في [1]، برنامج الاختبار [NUnit.cs]
- في [2،3]، سيقوم المشروع بإنشاء ملف DLL باسم [pam-dao-spring-nhibernate.dll]
- في [4]، الإشارة إلى مكتبة DLL لإطار عمل NUnit: [nunit.framework.dll]
- في [5]، لن يتم تضمين فئة [Main.cs] في مكتبة DLL [pam-dao-spring-nhibernate]
- في [6]، سيتم تضمين فئة [NUnit.cs] في مكتبة DLL [pam-dao-spring-nhibernate]
فئة اختبار NUnit هي كما يلي:
using System.Collections;
using NUnit.Framework;
using Pam.Dao.Service;
using Pam.Dao.Entites;
using Spring.Objects.Factory.Xml;
using Spring.Core.IO;
using Spring.Context.Support;
namespace Pam.Dao.Tests {
[TestFixture]
public class NunitPamDao : AssertionHelper {
// the [dao] layer to be tested
private IPamDao pamDao = null;
// manufacturer
public NunitPamDao() {
// layer instantiation [dao]
pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
}
// init
[SetUp]
public void Init() {
}
[Test]
public void GetAllIdentitesEmployes() {
// audit no. of employees
Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
}
[Test]
public void GetCotisations() {
// checking contribution rates
Cotisations cotisations = pamDao.GetCotisations();
Expect(3.49, EqualTo(cotisations.CsgRds).Within(1E-06));
Expect(6.15, EqualTo(cotisations.Csgd).Within(1E-06));
Expect(9.39, EqualTo(cotisations.Secu).Within(1E-06));
Expect(7.88, EqualTo(cotisations.Retraite).Within(1E-06));
}
[Test]
public void GetEmployeIdemnites() {
// individual verification
Employe employe1 = pamDao.GetEmploye("254104940426058");
Employe employe2 = pamDao.GetEmploye("260124402111742");
Expect("Jouveinal", EqualTo(employe1.Nom));
Expect(2.1, EqualTo(employe1.Indemnites.BaseHeure).Within(1E-06));
Expect("Laverti", EqualTo(employe2.Nom));
Expect(1.93, EqualTo(employe2.Indemnites.BaseHeure).Within(1E-06));
}
[Test]
public void GetEmployeIdemnites2() {
// non-existent individual verification
bool erreur = false;
try {
Employe employe1 = pamDao.GetEmploye("xx");
} catch {
erreur = true;
}
Expect(erreur, True);
}
}
}
- السطر 11: تحتوي الفئة على السمة [TestFixture]، مما يجعلها فئة اختبار [NUnit].
- السطر 12: الفئة مشتقة من فئة الأداة المساعدة AssertionHelper في إطار عمل NUnit (بدءًا من الإصدار 2.4.6).
- السطر 14: الحقل الخاص [pamDao] هو مثيل للواجهة التي توفر الوصول إلى طبقة [dao]. لاحظ أن نوع هذا الحقل هو واجهة، وليس فئة. وهذا يعني أن مثيل [pamDao] يجعل الطرق فقط قابلة للوصول — على وجه التحديد، تلك الموجودة في واجهة [IPamDao].
- الطرق التي يتم اختبارها في الفئة هي تلك التي تحمل السمة [Test]. بالنسبة لجميع هذه الطرق، تكون عملية الاختبار كما يلي:
- يتم تنفيذ الطريقة التي تحمل السمة [SetUp] أولاً. وهي تُستخدم لإعداد الموارد (اتصالات الشبكة، اتصالات قاعدة البيانات، إلخ) المطلوبة للاختبار.
- ثم يتم تنفيذ الطريقة المراد اختبارها
- وأخيرًا، يتم تنفيذ الطريقة التي تحمل السمة [TearDown]. تُستخدم هذه الطريقة عمومًا لتحرير الموارد التي خصصتها الطريقة التي تحمل السمة [SetUp].
- في اختبارنا، لا توجد موارد لتخصيصها قبل كل اختبار ثم إلغاء تخصيصها بعد ذلك. لذلك، لا نحتاج إلى طرق ذات السمتين [SetUp] و [TearDown]. في المثال، قمنا بتضمين طريقة ذات السمة [SetUp] في الأسطر 23–26.
- الأسطر 17-20: يقوم منشئ الفئة بتهيئة الحقل الخاص [pamDao] باستخدام Spring و [App.config].
- الأسطر 29-32: اختبار طريقة [GetAllIdentitesEmployes]
- الأسطر 35-42: اختبار طريقة [GetCotisations]
- الأسطر 45–53: اختبار طريقة [GetEmploye]
- الأسطر 56–65: اختبار طريقة [GetEmploye] عند حدوث استثناء.
يؤدي إنشاء المشروع إلى إنشاء ملف DLL [pam-dao-spring-nhibernate.dll] في المجلد [bin/Release]:
![]() |
نقوم بتحميل ملف DLL [pam-dao-spring-nhibernate.dll] باستخدام أداة [NUnit-Gui]، الإصدار 2.5، ثم نجري الاختبارات:

في الأعلى، كانت الاختبارات ناجحة.
2.2.4. إنشاء ملف DLL لطبقة [dao] باستخدام
بمجرد كتابة فئة [PamDaoNHibernate] واختبارها، سنقوم بإنشاء ملف DLL لطبقة [dao] على النحو التالي:
![]() |
- [1]، يتم استبعاد برامج الاختبار من تجميع المشروع
- [2،3]، تكوين المشروع
- [4]، إنشاء المشروع
- يتم إنشاء ملف DLL في المجلد [bin/Release] [5].











