13. تطبيق [SimuPaie] – الإصدار 9 – تكامل Spring/NHibernate
هنا، نقترح إعادة النظر في تطبيق ASP.NET ثلاثي الطبقات من الإصدار 7 [pam-v7-3tier-nhibernate-multivues-multipages]. كانت بنية التطبيق الطبقية كما يلي:
![]() |
في الإصدار أعلاه، تم تنفيذ طبقة [DAO] باستخدام إطار عمل NHibernate. تم استخدام إطار عمل Spring فقط لدمج الطبقات مع بعضها البعض. يوفر إطار عمل Spring فئات أدوات مساعدة للعمل مع إطار عمل NHibernate. استخدام هذه الفئات يجعل كتابة الكود في طبقة [DAO] أسهل. تتطور البنية السابقة على النحو التالي:
![]() |
نظرًا للهيكل الطبقي المستخدم، يتطلب تنفيذ تكامل Spring/NHibernate تعديل طبقة [DAO] فقط. لن تحتاج طبقات [Presentation] (الويب/ASP.NET) و[Business] إلى التعديل. هذه هي الميزة الرئيسية للبنى الطبقية المدمجة مع Spring.
في الأقسام التالية، سنقوم ببناء طبقة [DAO] باستخدام [Spring/NHibernate] من خلال شرح كود حل عملي. لن نحاول تغطية جميع خيارات التكوين أو استخدامات إطار عمل [Spring/NHibernate]. يمكن للقراء تكييف الحل المقترح مع احتياجاتهم الخاصة باستخدام وثائق Spring.NET [http://www.springframework.net/documentation.html] (يونيو 2010).
النهج المتبع لبناء طبقات [DAO] و[business] هو نهج الإصدار 3 الموصوف في الفقرة 7. أما النهج المتبع لطبقة [presentation] فهو نهج الإصدار 7 الموصوف في الفقرة 11.
13.1. طبقة الوصول إلى البيانات [DAO]
![]() |
13.1.1. مشروع Visual Studio C# لطبقة [dao]
مشروع Visual Studio لطبقة [dao] هو كما يلي:
![]() |
- في [1]، المشروع ككل
- يحتوي المجلد [pam] على فئات المشروع بالإضافة إلى تكوين كيانات NHibernate
- يقوم ملفا [App.config] و [Dao.xml] بتكوين إطار عمل Spring/NHibernate. سنحتاج إلى وصف محتويات هذين الملفين.
- في [2]، الفئات المختلفة للمشروع
- في المجلد [entities]، نجد كيانات NHibernate التي تمت دراستها في مشروع [pam-dao-nhibernate]
- في المجلد [service]، نجد واجهة [IPamDao] وتنفيذها باستخدام إطار عمل Spring/NHibernate [PamDaoSpringNHibernate]. سنحتاج إلى كتابة هذا التنفيذ الجديد لواجهة [IPamDao]
- يحتوي المجلد [tests] على نفس الاختبارات الموجودة في مشروع [pam-dao-nhibernate]. وهي تختبر نفس واجهة [IPamDao].
- في [3]، مراجع المشروع. يتطلب تكامل Spring/NHibernate ملفين DLL جديدين: [Spring.Data] و [Spring.Data.NHibernate12]. يتوفر هذان الملفان DLL في إطار عمل Spring.Net. وقد تمت إضافتهما إلى مجلد [lib] لملفات DLL [4]:
![]() |
في مراجع المشروع [3]، ستجد مكتبات DLL التالية:
- NHibernate: لـ ORM NHibernate
- MySql.Data: برنامج تشغيل ADO.NET لنظام إدارة قواعد البيانات MySQL
- Spring.Core: لإطار عمل Spring، الذي يتولى تكامل الطبقات
- log4net: مكتبة تسجيل
- nunit.framework: مكتبة اختبار الوحدات
- Spring.Data و Spring.Data.NHibernate12: يوفران دعم Spring/NHibernate.
تم أخذ هذه المراجع من المجلد [lib] [4]. تأكد من تعيين خاصية "Local Copy" لجميع هذه المراجع على "True" [5]:
13.1.2. تكوين مشروع C#
يتم تكوين المشروع على النحو التالي:
![]() |
- في [1]، اسم تجميع المشروع هو [pam-dao-spring-nhibernate]. يظهر هذا الاسم في ملفات تكوين المشروع المختلفة.
13.1.3. الكيانات في طبقة [dao]
![]() |
تم تجميع الكيانات (الكائنات) المطلوبة لطبقة [dao] في مجلد [entities] [1] الخاص بالمشروع. هذه الكيانات هي نفسها الموجودة في مشروع [pam-dao-nhibernate]، باستثناء واحد في ملفات تكوين 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>
- السطر 2: تشير سمة التجميع إلى أن ملف [Employee.hbm.xml] سيكون موجودًا في تجميع [pam-dao-spring-nhibernate]
13.1.4. تكوين Spring / NHibernate
لنعد إلى مشروع Visual C#:
![]() |
- في [1]، يقوم ملفا [App.config] و[Dao.xml] بتكوين تكامل Spring / NHibernate
13.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 محددة، سيوجد في هذا الملف.
13.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.NHibernate12">
<property name="DbProvider" ref="DbProvider"/>
<property name="MappingAssemblies">
<list>
<value>pam-dao-spring-nhibernate</value>
</list>
</property>
<property name="HibernateProperties">
<dictionary>
<entry key="hibernate.dialect" value="NHibernate.Dialect.MySQLDialect"/>
<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.NHibernate12">
<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 بتكوين SessionFactory في NHibernate، وهو الكائن المستخدم للحصول على جلسات NHibernate. لاحظ أن جميع عمليات قاعدة البيانات تتم ضمن جلسة NHibernate. في السطر 15، نلاحظ أن SessionFactory يتم تنفيذه بواسطة فئة Spring.Data.NHibernate.LocalSessionFactoryObject، الموجودة في ملف DLL Spring.Data.NHibernate12.
- السطر 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.NHibernate12. يتطلب هذا المدير معلمات اتصال قاعدة البيانات (السطر 34) بالإضافة إلى NHibernate SessionFactory (السطر 35).
- تحدد الأسطر 39-43 خصائص فئة HibernateTemplate، وهي أيضًا فئة Spring. ستُستخدم هذه الفئة كفئة مساعدة في الفئة التي تنفذ طبقة [DAO]. وهي تسهل التفاعلات مع كائنات NHibernate. تحتوي هذه الفئة على خصائص معينة يجب تهيئتها:
- السطر 40: NHibernate SessionFactory
- السطر 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.
13.1.5. تنفيذ طبقة [dao]
13.1.5.1. هيكل فئة التنفيذ
واجهة [IPamDao] هي نفسها الموجودة في مشروع [pam-dao-nhibernate]:
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.
13.1.5.2. طرق مفيدة لفئة HibernateTemplate
سنستخدم الطرق التالية لفئة HibernateTemplate:
IList<T> Find<T>(string hql_query) | تنفذ استعلام HQL وتُرجع قائمة من الكائنات من النوع T |
IList<T> Find<T>(string hql_query, object[]) | تنفيذ استعلام HQL معلمات بواسطة ?. يتم توفير قيم هذه المعلمات بواسطة مصفوفة الكائنات. |
IList<T> LoadAll<T>() | تُرجع جميع الكيانات من النوع T |
هناك طرق أخرى مفيدة لن تتاح لنا الفرصة لاستخدامها، والتي تتيح لك استرداد الكيانات وحفظها وتحديثها وحذفها:
T Load<T>(object id) | تضيف الكيان من النوع T الذي يحمل المفتاح الأساسي id إلى جلسة عمل NHibernate. |
void SaveOrUpdate(object entity) | يقوم بإدراج (INSERT) أو تحديث (UPDATE) كائن الكيان اعتمادًا على ما إذا كان يحتوي على مفتاح أساسي (UPDATE) أم لا (INSERT). يمكن تكوين عدم وجود مفتاح أساسي عبر السمة `unsaved-values` في ملف تكوين الكيان. بعد عملية `SaveOrUpdate`، يكون كائن الكيان موجودًا في جلسة NHibernate. |
void Delete(object entity) | يزيل كائن الكيان من جلسة NHibernate. |
13.1.5.3. تنفيذ طريقة init
طريقة init لفئة [PamDaoSpringNHibernate] هي، حسب التكوين، الطريقة التي يتم تنفيذها بعد أن يقوم Spring بإنشاء مثيل للفئة. والغرض منها هو تخزين هويات الموظفين المبسطة (اللقب، الاسم الأول، رقم الضمان الاجتماعي) ومعدلات الاشتراكات محليًا. ويمكن أن يكون كودها كما يلي.
[Transaction(ReadOnly = true)]
public void init() {
try {
// on récupère la liste simplifiée des employés
IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
// on la met dans un tableau
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++;
}
// on met les taux de cotisations dans un objet
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 بتهيئة الحقلين الخاصين بالفئة.
13.1.5.4. تنفيذ طريقة GetEmployee
يجب أن تُرجع طريقة GetEmployee كيان Employee برقم ضمان اجتماعي معين. قد يكون كودها كما يلي:
[Transaction(ReadOnly = true)]
public Employe GetEmploye(string ss) {
IList<Employe> employés = null;
try {
// requête
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);
}
// a-t-on récupéré un employé ?
if (employés.Count == 0) {
// on signale le fait
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: في هذه الحالة، نُرجع الموظف الأول في القائمة
13.1.5.5. الخلاصة
إذا قارنا كود طبقة [DAO] عند استخدام
- إطار عمل Spring / NHibernate
- إطار عمل Spring / NHibernate
، ندرك أن الحل الثاني سمح بكتابة كود أبسط.
13.2. اختبار طبقة [DAO]
13.2.1. مشروع Visual Studio
تم عرض مشروع Visual Studio بالفعل. للتذكير:
![]() |
- في [1]، المشروع ككل
- في [2]، الفئات المختلفة للمشروع. يحتوي المجلد [tests] على اختبار وحدة التحكم [Main.cs] واختبار الوحدة [NUnit.cs].
- في [3]، يتم ترجمة البرنامج [Main.cs].
![]() |
- في [4]، لم يتم إنشاء ملف [NUnit.cs].
- المشروع عبارة عن تطبيق وحدة تحكم. الفئة التي يتم تنفيذها هي تلك المحددة في [5]، وهي الفئة الموجودة في ملف [Main.cs].
13.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] الذي تمت مناقشته في القسم 13.1.4.1.
يؤدي تشغيل الكود مع قاعدة البيانات الموضحة في القسم 6.2 إلى إخراج وحدة التحكم التالي:
- السطران 1-2: الموظفان من النوع [Employee] مع المعلومات [SS، Last Name، First Name] فقط
- السطر 4: الموظف من النوع [Employee] الذي يحمل رقم الضمان الاجتماعي [254104940426058]
- السطر 5: معدلات الاشتراكات
13.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);
}
}
}
تمت مناقشة هذه الفئة بالفعل في القسم 7.3.4.
يؤدي إنشاء المشروع إلى إنشاء ملف DLL [pam-dao-spring-nhibernate.dll] في المجلد [bin/Release].
![]() |
نقوم بتحميل ملف DLL [pam-dao-spring-nhibernate.dll] باستخدام أداة [NUnit-Gui]، الإصدار 2.4.6، ونقوم بتشغيل الاختبارات:

في الأعلى، كانت الاختبارات ناجحة.
تمرين عملي:
تنفيذ الاختبارات الخاصة بفئة [PamDaoSpringNHibernate] على جهاز.- استخدم ملفات تكوين مختلفة [Dao.xml] لاستخدام أنظمة إدارة قواعد البيانات الأخرى (Firebird، MySQL، Postgres، SQL Server)
13.2.4. إنشاء ملف DLL لطبقة [dao]
بمجرد كتابة فئة [PamDaoNHibernate] واختبارها، سيتم إنشاء DLL لطبقة [dao] على النحو التالي:
![]() |
- [1]، يتم استبعاد برامج الاختبار من تجميع المشروع
- [2،3]، تكوين المشروع
- [4]، بناء المشروع
- يتم إنشاء ملف DLL في المجلد [bin/Release] [5]. نضيفه إلى ملفات DLL الموجودة بالفعل في المجلد [lib] [6]:
![]() |
13.3. طبقة الأعمال
دعونا نعيد النظر في البنية العامة لتطبيق [SimuPaie]:
![]() |
نفترض الآن أن طبقة [dao] قد اكتملت وتم تغليفها في مكتبة DLL [pam-dao-spring-nhibernate.dll]. سنركز الآن على طبقة [الأعمال]. هذه هي الطبقة التي تنفذ قواعد الأعمال، وفي هذه الحالة قواعد حساب الراتب.
قد يبدو مشروع Visual Studio الخاص بطبقة الأعمال كما يلي:
![]() |
- في [1]، يتم تكوين المشروع بأكمله بواسطة ملفَي [App.config] و[Dao.xml]. ملف [App.config] مطابق لما كان عليه في مشروع طبقة [dao] [pam-dao-spring-nhibernate]. وينطبق الأمر نفسه على ملف [Dao.xml]، باستثناء أنه يعلن عن كائن Spring إضافي بمعرف pammetier. إعلان هذا الكائن مطابق لما كان عليه في ملف [App.config] لمشروع [pam-metier-dao-nhibernate].
- في [2]، المجلد [pam] مطابق لما كان عليه في طبقة [metier] لمشروع [pam-metier-dao-nhibernate]
- في [3]، المراجع المستخدمة من قبل المشروع. لاحظ ملف DLL [pam-dao-spring-nhibernate] من طبقة [dao] التي تمت مناقشتها سابقًا.
السؤال: قم ببناء مشروع [pam-metier-dao-spring-nhibernate] أعلاه. سيتم اختباره بشكل منفصل:
-
في وضع وحدة التحكم بواسطة برنامج وحدة التحكم [Main.cs]
-
بواسطة اختبار الوحدة [NUnit.cs] الذي يتم تنفيذه بواسطة إطار عمل NUnit
يمكن إنشاء المشروع الجديد [pam-metier-dao-spring-nhibernate] ببساطة عن طريق نسخ مشروع [pam-metier-dao-nhibernate] ثم تعديل العناصر التي تحتاج إلى تغيير.
بعد الاختبار، سنقوم بإنشاء ملف DLL لطبقة [business]، والذي سنسميه [pam-metier-dao-spring-nhibernate]:
![]() |
- في [1]، اختبار NUnit الناجح
- في [2]، ملف DLL الذي تم إنشاؤه بواسطة المشروع
سنضيف ملف DLL الخاص بطبقة [business] إلى ملفات DLL الموجودة بالفعل في مجلد [lib] [3]:
![]() |
13.4. طبقة [web]
دعونا نعيد النظر في البنية العامة لتطبيق [SimuPaie]:
![]() |
نفترض أن طبقتي [dao] و[business] مكتملتان ومغلفتان في مكتبات DLL [pam-dao-spring-nhibernate، pam-business-dao-spring-nhibernate]. سنقوم الآن بوصف طبقة الويب.
يتم الحصول على مشروع Visual Web Developer الخاص بطبقة [web] أولاً عن طريق نسخ مجلد مشروع الويب [pam-v7-3tier-nhibernate-multivues-multipages] ببساطة. ثم يتم تغيير اسم المشروع إلى [pam-v9-3tier-spring-nhibernate-multivues-multipages]:
![]() |
يختلف مشروع الويب الجديد [pam-v9-3tier-spring-nhibernate-multivues-multipages] عن مشروع [pam-v7-3tier-nhibernate-multivues-multipages] في النواحي التالية:
- في [1]، يتم تكوينه بواسطة ملفات [Dao.xml] و [Web.config]. لم يكن ملف [Dao.xml] موجودًا في [pam-v7]، ويجب أن يتضمن ملف [Web.config] تكوين Spring/NHibernate، بينما في [pam-v7]، تم تكوين NHibernate فقط.
- في [2]، ملفات DLL لطبقات [dao] و[business] هي تلك التي أنشأناها للتو.
ملف [Dao.xml] هو الملف المستخدم في إنشاء طبقة [business]. ملف [Web.config] هو الملف الموجود في [pam-v7]، والذي نضيف إليه تكوين Spring/NHibernate الموجود في ملفات [App.config] لطبقتي [dao] و[business]. ملف [Web.config] لـ [pam-v9] هو كما يلي:
<configuration>
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
........
</sectionGroup>
<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>
............. le reste est identique au fichier [Web.config] de [pam-v7]
تحتوي الأسطر 7–11 و16–23 على تكوين Spring الذي كان موجودًا في ملفات [App.config] لطبقات [dao] و[business] التي تم إنشاؤها مسبقًا، مع اختلاف واحد: في ملفات [App.config]، تمت كتابة السطر 17 على النحو التالي:
<resource uri="Dao.xml" />
مع التكوين التالي:
![]() |
يتم نسخ ملف [Dao.xml] إلى المجلد [bin] داخل مجلد مشروع الويب. باستخدام الصيغة
<resource uri="Dao.xml" />
سيتم البحث عن ملف [Dao.xml] في الدليل الحالي للعملية التي تقوم بتشغيل تطبيق الويب. وتبين أن هذا الدليل ليس مجلد [bin] الخاص بمشروع الويب الذي يتم تنفيذه. يجب عليك كتابة:
<resource uri="~/Dao.xml" />
بحيث يتم البحث عن ملف [Dao.xml] في مجلد [bin] الخاص بمشروع الويب الذي يتم تنفيذه.
السؤال: قم بنشر هذا التطبيق الويب على جهاز.
13.5. الخلاصة
لقد انتقلنا من البنية:
![]() |
إلى البنية:
![]() |
كان الهدف هو تنفيذ طبقة [DAO] من خلال الاستفادة من الإمكانات التي يوفرها تكامل Spring مع NHibernate.
لاحظنا أن هذا:
- أثر على طبقة [DAO]. كانت هذه الطبقة أسهل في الكتابة ولكنها تطلبت تكوينًا أكثر تعقيدًا لـ Spring.
- أثر بشكل هامشي على طبقتي [business] و [web]
وقد قدم هذا مثالاً آخر على مزايا البنى ذات الطبقات.























