Skip to content

2. یکپارچه‌سازی Spring / NHibernate

چارچوب Spring کلاس‌های کمکی برای کار با چارچوب NHibernate فراهم می‌کند. استفاده از این کلاس‌ها نوشتن کد دسترسی به داده‌ها از SGBD را آسان‌تر می‌کند. معماری چندلایه زیر را در نظر بگیرید:

در ادامه، ما یک لایه [dao] را با استفاده از [Spring / NHibernate] خواهیم ساخت و در عین حال، در مورد کد یک راه‌حل عملی توضیح می‌دهیم. ما تلاش نمی‌کنیم تا تمام پیکربندی‌ها یا کاربردهای ممکن چارچوب [Spring / Nhibernate] را پوشش دهیم. خوانندگان می‌توانند با کمک مستندات Spring.NET و [Spring.NET | Homepage ] (دسامبر ۲۰۱۱)، راه‌حل پیشنهادی را برای مشکلات خود تطبیق دهند.

2.1. لایه دسترسی به داده [dao]

پایگاه داده همان پایگاه داده MySQL [dbpam_nhibernate] است که در بخش 1.2 قبلاً توصیف شده است. لایه [dao] رابط C# زیر را پیاده‌سازی می‌کند:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        //فهرست تمام سوابق کارکنان 
        Employe[] GetAllIdentitesEmployes();
        // یک کارمند مشخص و مزایای او 
        Employe GetEmploye(string ss);
        // فهرست تمام مشارکت‌ها 
        Cotisations GetCotisations();
    }
}

2.1.1. پروژهٔ C# ویژوال استودیو برای لایهٔ [dao]

پروژه ویژوال استودیو برای لایه [dao] به شرح زیر است:

  • در [1]، پروژه به طور کلی
    • پوشه [pam] شامل کلاس‌های پروژه و همچنین پیکربندی اجزای NHibernate است
    • فایل‌های [App.config] و [Dao.xml] چارچوب Spring / NHibernate را پیکربندی می‌کنند. ما باید محتوای این دو فایل را توضیح دهیم.
  • در [2]، کلاس‌های مختلف پروژه
    • در پوشه [entites]، انتیت‌های NHibernate را که در پروژه قبلی مطالعه شده‌اند، می‌یابیم (به صفحه 14 مراجعه کنید)
    • در پوشه [service]، رابط [IPamDao] و پیاده‌سازی آن با استفاده از چارچوب Spring / NHibernate [PamDaoSpringNHibernate] را می‌یابیم.
    • پوشه [tests] شامل تست‌های رابط [IPamDao] است.
  • در [3]، وابستگی‌های پروژه. یکپارچه‌سازی Spring (NHibernate) به فایل‌های جدید DLL و [4] نیاز دارد.

وابستگی‌های پروژه [3] شامل فایل‌های زیر DLL است:

  • NHibernate: برای ORM و NHibernate
  • MySql.Data: درایور ADO.NET برای SGBD MySQL 5
  • Spring.Core: برای فریم‌ورک Spring که یکپارچه‌سازی لایه‌ها را مدیریت می‌کند
  • log4net: یک کتابخانهٔ لاگ‌گیری
  • nunit.framework: کتابخانه تست واحد
  • Spring.Aop، Spring.Data و Spring.Data.NHibernate32: پشتیبانی از Spring / NHibernate را فراهم می‌کنند.

ما اطمینان حاصل خواهیم کرد که تمام این فایل‌های DLL دارای خاصیت «Local Copy» با مقدار True باشند.

2.1.2. پیکربندی پروژهٔ C#

پروژه به شرح زیر پیکربندی شده است:

  • در [1]، نام اسمبلی پروژه [pam-dao-spring-nhibernate] است. این نام در فایل‌های مختلف پیکربندی پروژه ظاهر می‌شود.

2.1.3. اشیاء در لایه [dao]

اشیاء (آبجکت‌ها) مورد نیاز برای لایه [dao] در پوشه [entites] [1] پروژه گردآوری شده‌اند. این اشیاء مشابه اشیاء پروژه قبلی هستند (به بخش 1.3.2 مراجعه کنید)، با یک تفاوت در فایل‌های پیکربندی NHibernate. به‌عنوان مثال، فایل [Employe.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>
  • خط ۳: ویژگی 'assembly' نشان می‌دهد که فایل [Employe.hbm.xml] در اسمبلی [pam-dao-spring-nhibernate] یافت خواهد شد

علاوه بر این، در پوشه [entites]، یک کلاس استثنا (exception) که توسط پروژه استفاده می‌شود، یافت می‌شود:


using System;
namespace Pam.Dao.Entites {

    public class PamException : Exception {

        // کد خطا 
        public int Code { get; set; }

        // تولیدکنندگان 
        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] (خط ۴) مشتق شده است تا یک کد خطا (خط ۷) به آن اضافه شود.

2.1.4. پیکربندی Spring / NHibernate

بیایید به پروژه ویژوال سی‌شارپ بازگردیم:

  • در [1]، فایل‌های [App.config] و [Dao.xml] یکپارچه‌سازی Spring / NHibernate را پیکربندی می‌کنند.

2.1.4.1. فایل [App.config]

فایل [App.config] به شرح زیر است:


<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <!--بخش‌های پیکربندی -->
    <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 -->
    <spring>
        <parsers>
            <parser type="Spring.Data.Config.DatabaseNamespaceParser, Spring.Data" />
        </parsers>
        <context>
            <resource uri="Dao.xml" />
        </context>
    </spring>

    <!-- این بخش شامل تنظیمات پیکربندی log4net است -->
    <!--NOTE IMPORTANTE: لاگ‌ها به‌طور پیش‌فرض فعال نیستند. باید به‌صورت برنامه‌ای فعال شوند
    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>

        <!-- سطح پیش‌فرض لاگ‌گیری را روی DEBUG تنظیم کنید -->
        <root>
            <level value="DEBUG" />
            <appender-ref ref="ConsoleAppender" />
        </root>

        <!-- ثبت‌رویداد را برای Spring تنظیم کنید.  نام‌های لاگر در Spring با فضای نام مطابقت دارند -->
        <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] (خط ۲۰) پیکربندی شده‌اند. بنابراین پیکربندی 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">

    <!-- اشاره شده توسط فایل پیکربندی زمینهٔ اصلی برنامه -->
    <description>
        Application Spring / NHibernate
    </description>

    <!--پیکربندی پایگاه داده و NHibernate -->
    <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>

    <!-- مدیر تراکنش -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate32">
        <property name="DbProvider" ref="DbProvider"/>
        <property name="SessionFactory" ref="NHibernateSessionFactory"/>
    </object>

    <!-- قالب Hibernate -->
    <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>

    <!-- ابجکت‌های دسترسی به داده‌ها -->
    <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>
  • خطوط ۱۱–۱۳ اتصال به پایگاه داده [dbpam_nhibernate] را پیکربندی می‌کنند. این شامل:
    • پروویدر ADO.NET مورد نیاز برای اتصال؛ در این مورد، پروویدر برای SGBD، MySQL است. این بدان معناست که DLL و [Mysql.Data] باید در مراجع پروژه گنجانده شوند.
    • رشته اتصال پایگاه داده (سرور، نام پایگاه داده، مالک اتصال، رمز عبور)
  • خطوط ۱۵–۲۹ SessionFactory از NHibernate را پیکربندی می‌کنند، ابجکتی که برای به‌دست‌آوردن جلسات NHibernate استفاده می‌شود. شایان ذکر است که تمام عملیات پایگاه داده در داخل یک جلسه NHibernate انجام می‌شوند. در خط ۱۵، می‌بینیم که 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] پیکربندی شده‌اند تا در مونتاژ پروژه گنجانده شوند.
  • خطوط ۲۲–۲۷: ویژگی‌های خاص NHibernate.
    • خط ۲۴: گویش SQL مورد استفاده، گویش MySQL خواهد بود.
    • خط ۲۵: خروجی SQL که توسط NHibernate تولید می‌شود، در گزارش‌های کنسول نمایش داده نخواهد شد. تنظیم این خاصیت روی true به شما امکان می‌دهد تا دستورات SQL صادر شده توسط NHibernate را مشاهده کنید. این می‌تواند به شما کمک کند تا برای مثال، دلیل کند بودن اجرای یک برنامه هنگام دسترسی به پایگاه داده را درک کنید.
  • خط ۲۸: تنظیم این ویژگی از ExposeTransactionAwareSessionFactory به true باعث می‌شود Spring anotationهای مدیریت تراکنش موجود در کد C# را مدیریت کند. ما هنگام نوشتن کلاسی که لایه [dao] را پیاده‌سازی می‌کند، به این موضوع باز خواهیم گشت.
  • خطوط ۳۲–۳۶ مدیر تراکنش را تعریف می‌کنند. مجدداً، این مدیر یک کلاس Spring از نوع DLL Spring.Data.NHibernate32 است. این مدیر باید پارامترهای اتصال به پایگاه داده (خط ۳۴) و همچنین SessionFactory و NHibernate (خط ۳۵) را بشناسد.
  • خطوط 39–43 ویژگی‌های کلاس HibernateTemplate را تعریف می‌کنند که آن نیز یک کلاس Spring است. این کلاس به‌عنوان یک کلاس ابزار (utility class) در داخل کلاسی که لایه [dao] را پیاده‌سازی می‌کند، استفاده خواهد شد. این کلاس تعامل با اشیاء NHibernate را تسهیل می‌کند. این کلاس دارای برخی ویژگی‌ها است که باید مقداردهی اولیه شوند:
    • خط ۴۰: ویژگی SessionFactory از NHibernate
    • خط ۴۱: خاصیت TemplateFlushMode حالت همگام‌سازی زمینه پایداری NHibernate با پایگاه داده را تنظیم می‌کند. حالت Auto تضمین می‌کند که همگام‌سازی انجام خواهد شد:
      • در پایان یک تراکنش
      • قبل از یک عملیات select
    • خط ۴۲: پرس‌وجوهای HQL (زبان پرس‌وجوی Hibernate) پنهان‌سازی (کش) خواهند شد. این امر ممکن است منجر به بهبود عملکرد شود.
  • خطوط ۴۶–۴۸ کلاس پیاده‌سازی لایه [dao] را تعریف می‌کنند
    • خط ۴۶: لایه [dao] توسط کلاس [PamdaoSpringNHibernate] از DLL [pam-dao-spring-nhibernate] پیاده‌سازی خواهد شد. پس از instantiate شدن کلاس، متد init کلاس بلافاصله اجرا خواهد شد. وقتی کانtejner Spring خاموش می‌شود، متد destroy کلاس اجرا خواهد شد.
    • خط ۴۷: کلاس [PamDaoSpringNHibernate] دارای یک خصوصیت HibernateTemplate خواهد بود که با خصوصیت HibernateTemplate از خط ۳۹ مقداردهی اولیه می‌شود.

2.1.5. پیاده‌سازی لایه [dao]

2.1.5.1. اسکلت کلاس پیاده‌سازی

رابط [IPamDao] به شرح زیر است:


using Pam.Dao.Entites;

namespace Pam.Dao.Service {
    public interface IPamDao {
        // فهرست تمام شناسه کارمندان 
        Employe[] GetAllIdentitesEmployes();
        // یک کارمند مشخص به همراه مزایای او 
        Employe GetEmploye(string ss);
        //فهرست تمام مشارکت‌ها 
        Cotisations GetCotisations();
    }
}
  • خط ۱: فضای نام برای اجزا در لایه [dao] وارد می‌شود.
  • خط ۳: لایه [dao] در فضای نام [Pam.Dao.Service] قرار دارد. عناصر در فضای نام [Pam.Dao.Entites] می‌توانند در چندین نمونه ایجاد شوند. عناصر در فضای نام [Pam.Dao.Service] به صورت یک نمونه واحد (singleton) ایجاد می‌شوند. این همان چیزی است که انتخاب نام‌های فضای نام را توجیه می‌کند.
  • خط ۴: این رابط [IPamDao] نامیده می‌شود. آن سه متد را تعریف می‌کند:
    • خط ۶: [GetAllIdentitesEmployes] آرایه‌ای از اشیاء از نوع [Employe] را بازمی‌گرداند که فهرست مراقبان کودک را به شکلی ساده‌شده (نام خانوادگی، نام، SS) نشان می‌دهد.
    • خط ۸، [GetEmploye] یک شیء [Employe] را بازمی‌گرداند: کارمندی با شماره تأمین اجتماعی که به‌عنوان پارامتر به متد ارسال شده است، همراه با مزایای مرتبط با شاخص او.
    • خط ۱۰: [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 {
        // fields خصوصی 
        private Cotisations cotisations;
        private Employe[] employes;

        // ابتدا 
        [Transaction(ReadOnly = true)]
        public void init() {
...
        }

        // حذف شیء
        public void destroy() {
            if (HibernateTemplate.SessionFactory != null) {
                HibernateTemplate.SessionFactory.Close();
            }
        }

        // فهرست تمام شناسه کارمندان
        public Employe[] GetAllIdentitesEmployes() {
            return employes;
        }


        // یک کارمند مشخص به همراه مزایای او 
        [Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
....
        }

        // فهرست مشارکت‌ها 
        public Cotisations GetCotisations() {
            return cotisations;
        }
    }
}
  • خط ۹: کلاس [PamDaoSpringNHibernate] رابط [dao] [IPamDao] را به درستی پیاده‌سازی می‌کند. این کلاس همچنین از کلاس Spring با نام [HibernateDaoSupport] ارث می‌برد. این کلاس دارای یک خاصیت [HibernateTemplate] است که توسط پیکربندی Spring که راه‌اندازی شده است، مقداردهی اولیه می‌شود (خط ۲ زیر):

    <object id="pamdao" type="Pam.Dao.Service.PamDaoSpringNHibernate, pam-dao-spring-nhibernate" init-method="init" destroy-method="destroy">
        <property name="HibernateTemplate" ref="HibernateTemplate"/>
</object>
  • در خط ۱ بالا، می‌بینیم که تعریف شیء [pamdao] مشخص می‌کند که متدهای init و destroy از کلاس [PamDaoSpringNHibernate] باید در زمان‌های مشخصی اجرا شوند. این دو متد در واقع در کلاس در خطوط 16 و 21 موجود هستند.
  • خطوط ۱۵ و ۳۴: آْنوتیشن‌هایی که تضمین می‌کنند متد علامت‌گذاری‌شده در داخل یک تراکنش اجرا شود. ویژگی ReadOnly=true نشان می‌دهد که تراکنش فقط-خواندنی است. ممکن است متدی که در داخل تراکنش اجرا می‌شود، یک استثنا پرتاب کند. در این صورت، اسپرینگ به‌طور خودکار تراکنش را کامیت می‌کند (Rollback). این آنوتیشن نیاز به مدیریت تراکنش در داخل متد را از بین می‌برد.
  • خط ۱۶: متد init بلافاصله پس از instantiate شدن کلاس توسط Spring اجرا می‌شود. خواهیم دید که هدف آن initialise کردن فیلدهای private در خطوط ۱۱ و ۱۲ است. این متد در داخل یک transaction (خط ۱۵) اجرا خواهد شد.
  • متدهای رابط [IPamDao] در خطوط 28، 35 و 40 پیاده‌سازی شده‌اند.
  • خطوط ۲۸–۳۰: متد [GetAllIdentitesEmployes] به سادگی ویژگی خط ۱۲ را که توسط متد init مقداردهی اولیه شده است، برمی‌گرداند.
  • خطوط ۴۰–۴۲: متد [GetCotisations] به سادگی ویژگی را از خط ۱۱ بازمی‌گرداند که توسط متد init مقداردهی اولیه شده بود.

2.1.5.2. متدهای مفید کلاس HibernateTemplate

ما از متدهای زیر از کلاس HibernateTemplate استفاده خواهیم کرد:

IList<T> Find<T>(string requete_hql)
پرس‌وجوی HQL را اجرا می‌کند و فهرستی از اشیاء از نوع T را بازمی‌گرداند
IList<T> Find<T>(string requete_hql, object[])
یک پرس‌وجو HQL را با پارامترهای مشخص‌شده با ؟ اجرا می‌کند. مقادیر این پارامترها توسط آرایهٔ اشیاء تأمین می‌شوند.
IList<T> LoadAll<T>()
تمام موجودیت‌های از نوع T را بازمی‌گرداند

روش‌های مفید دیگری نیز وجود دارند که فرصت استفاده از آن‌ها را نخواهیم داشت، که به شما امکان بازیابی، ذخیره، به‌روزرسانی و حذف اشیاء را می‌دهند:

T Load<T>(object id)
این انتیت را با نوع T و کلید اصلی id به جلسه NHibernate اضافه می‌کند.
void SaveOrUpdate(object entité)
شیء entité را درج (INSERT) یا به‌روزرسانی (UPDATE) می‌کند، بسته به اینکه آیا کلید اصلی (UPDATE) دارد یا خیر (INSERT). نبود کلید اصلی را می‌توان از طریق ویژگی unsaved-values در فایل پیکربندی موجوده پیکربندی کرد. پس از عملیات SaveOrUpdate، شیء entité در جلسه NHibernate قرار دارد.
void Delete(object entité)
شیء entité را از جلسه NHibernate حذف می‌کند.

2.1.5.3. پیاده‌سازی متد init

متد init از کلاس [PamDaoSpringNHibernate]، بر اساس پیکربندی، متدی است که پس از ایجاد نمونه از کلاس توسط Spring اجرا می‌شود. هدف آن کش کردن شناسه‌های ساده‌شده کارمندان (نام خانوادگی، نام، SS) و نرخ‌های مشارکت به‌صورت محلی است. کد آن ممکن است به شکل زیر باشد.


[Transaction(ReadOnly = true)]
        public void init() {
            try {
                // بازیابی فهرست ساده‌شده کارمندان
                IList<object[]> lignes = HibernateTemplate.Find<object[]>("select e.SS,e.Nom,e.Prenom from Employe e");
                // در یک جدول قرار می‌گیرد
                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++;
                }
                // نرخ‌های مشارکت در یک شیء قرار داده می‌شوند 
                cotisations = (HibernateTemplate.LoadAll<Cotisations>())[0];
            } catch (Exception ex) {
                //تبدیل استثنا 
                throw new PamException(string.Format("Erreur d'accès à la BD : [{0}]", ex.ToString()), 43);
            }
        }
  • خط ۵: یک پرس‌وجوی HQL اجرا می‌شود. این پرس‌وجو فیلدهای SS، نام خانوادگی و نام کوچک را برای تمام اشیاء Employé بازیابی می‌کند. این پرس‌وجو یک لیست از اشیاء را برمی‌گرداند. اگر ما کل رکورد کارمند را با عبارت «select e from Employee e» درخواست می‌کردیم، فهرستی از اشیاء از نوع Employe دریافت می‌کردیم.
  • خطوط ۷–۱۲: این لیست اشیاء در یک آرایه از اشیاء از نوع Employe کپی می‌شود.
  • خط 14: ما فهرست تمام موجودیت‌های نوع Cotisations را درخواست می‌کنیم. می‌دانیم که این فهرست تنها یک عنصر دارد. بنابراین عنصر اول فهرست را برای به‌دست‌آوردن نرخ‌های مشارکت بازیابی می‌کنیم.
  • خطوط ۷ و ۱۴ دو فیلد خصوصی کلاس را مقداردهی اولیه می‌کنند.

2.1.5.4. پیاده‌سازی متد GetEmploye

متد GetEmploye باید انتیتای Employee را با شمارهٔ SS مشخص بازگرداند. کد آن می‌تواند به صورت زیر باشد:


[Transaction(ReadOnly = true)]
        public Employe GetEmploye(string ss) {
            IList<Employe> employés = null;
            try {
                // پرس‌وجو
                employés = HibernateTemplate.Find<Employe>("select e from Employe e where e.SS=?", new object[]{ss});
            } catch (Exception ex) {
                // استثنا را تبدیل کنید 
                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);
            }
            //آیا کارمند بازیابی شده است؟ 
            if (employés.Count == 0) {
                // گزارش واقعیت 
                throw new PamException(string.Format("L'employé de n° ss [{0}] n'existe pas", ss), 42);
            } else {
                return employés[0];
            }
        }
  • خط ۶: فهرست کارمندان با شمارهٔ SS را بازیابی می‌کند
  • خط ۱۲: معمولاً، اگر کارمند وجود داشته باشد، باید یک لیست شامل یک عنصر واحد بازگردانده شود
  • خط ۱۴: اگر اینطور نباشد، یک استثنا پرتاب می‌شود
  • خط ۱۶: اگر این‌طور باشد، اولین کارمند لیست را بازگردانید

2.2. تست‌ها برای لایه [dao]

2.2.1. پروژه ویژوال استودیو

پروژهٔ ویژوال استودیو قبلاً ارائه شده است. برای مرور:

  • در [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 {
                // نمونه سازی لایه [dao]
                IPamDao pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
                // فهرست هویت کارکنان 
                foreach (Employe Employe in pamDao.GetAllIdentitesEmployes()) {
                    Console.WriteLine(Employe.ToString());
                }
                // یک کارمند به همراه مزایای او 
                Console.WriteLine("------------------------------------");
                Console.WriteLine(pamDao.GetEmploye("254104940426058"));
                Console.WriteLine("------------------------------------");
                // فهرست مشارکت‌ها 
                Cotisations cotisations = pamDao.GetCotisations();
                Console.WriteLine(cotisations.ToString());
            } catch (Exception ex) {
                //نمایش استثنا 
                Console.WriteLine(ex.ToString());
            }
            //پایان 
            Console.ReadLine();
        }
    }
}
  • خط ۱۱: از Spring خواسته می‌شود تا مرجعی به لایه [dao] ارائه دهد.
  • خطوط ۱۳–۱۵: تست روش [GetAllIdentitesEmployes] از رابط [IPamDao]
  • خط ۱۸: تست متد [GetEmploye] از رابط [IPamDao]
  • خط ۲۱: آزمون متد [GetCotisations] از رابط [IPamDao]

Spring، NHibernate و log4net توسط فایل [App.config] که در بخش 2.1.4.1 مورد بحث قرار گرفته است، پیکربندی می‌شوند.

اجرای برنامه با پایگاه‌داده توصیف‌شده در بخش 1.2 خروجی کنسول زیر را تولید می‌کند:

1
2
3
4
5
6
[254104940426058,Jouveinal,Marie,,,,]
[260124402111742,Laverti,Justine,,,,]
------------------------------------
[254104940426058,Jouveinal,Marie,5 rue des oiseaux,St Corentin,49203,[2, 2,1, 2,1, 3,1, 15]]
------------------------------------
[3,49,6,15,9,39,7,88]
  • خطوط ۱–۲: دو کارمند از نوع [Employe]، با اطلاعات مربوط به [SS, Nom, Prenom]
  • خط ۴: کارمند از نوع [Employe] با شماره تأمین اجتماعی [254104940426058]
  • خط ۵: نرخ‌های مشارکت

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 {
        //لایه [dao] برای آزمایش 
        private IPamDao pamDao = null;

        // سازنده 
        public NunitPamDao() {
            // نمونهسازی لایه [dao]
            pamDao = (IPamDao)ContextRegistry.GetContext().GetObject("pamdao");
        }

        // ابتدا 
        [SetUp]
        public void Init() {

        }

        [Test]
        public void GetAllIdentitesEmployes() {
            //بررسی تعداد کارمندان 
            Expect(2, EqualTo(pamDao.GetAllIdentitesEmployes().Length));
        }

        [Test]
        public void GetCotisations() {
            // بررسی نرخ مشارکت 
            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() {
            // تأیید فرد 
            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() {
            //بررسی فرد وجود ندارد 
            bool erreur = false;
            try {
                Employe employe1 = pamDao.GetEmploye("xx");
            } catch {
                erreur = true;
            }
            Expect(erreur, True);
        }
    }
}
  • خط ۱۱: این کلاس دارای ویژگی [TestFixture] است که آن را به یک کلاس تست [NUnit] تبدیل می‌کند.
  • خط ۱۲: این کلاس از کلاس ابزار AssertionHelper در چارچوب NUnit (از نسخه ۲.۴.۶ به بعد) ارث می‌برد.
  • خط 14: فیلد خصوصی [pamDao] نمونه‌ای از رابط دسترسی لایه [dao] است. توجه داشته باشید که نوع این فیلد یک اینترفیس است و نه یک کلاس. این بدان معناست که اینستنس [pamDao] تنها متدها را در دسترس قرار می‌دهد، یعنی متدهای اینترفیس [IPamDao].
  • متدهای مورد آزمایش در کلاس، آنهایی هستند که دارای ویژگی [Test] می‌باشند. برای تمام این متدها، فرآیند تست به شرح زیر است:
    • ابتدا متدی که دارای ویژگی [SetUp] است اجرا می‌شود. این متد برای آماده‌سازی منابع (اتصالات شبکه، اتصالات پایگاه داده و غیره) مورد نیاز برای تست استفاده می‌شود.
    • سپس متد مورد آزمایش اجرا می‌شود
    • و در نهایت، متدی که دارای ویژگی [TearDown] است، اجرا می‌شود. این متد عموماً برای آزادسازی منابعی استفاده می‌شود که توسط متد دارای ویژگی [SetUp] تخصیص داده شده‌اند.
  • در تست ما، هیچ منبعی برای تخصیص دادن قبل از هر تست و سپس آزاد کردن آن پس از آن وجود ندارد. بنابراین، ما به متدهایی با ویژگی‌های [SetUp] و [TearDown] نیازی نداریم. برای مثال، در خطوط ۲۳–۲۶، متدی با ویژگی [SetUp] را نشان داده‌ایم.
  • خطوط 17–20: سازنده کلاس فیلد خصوصی [pamDao] را با استفاده از Spring و [App.config] مقداردهی اولیه می‌کند.
  • خطوط ۲۹–۳۲: تست متد [GetAllIdentitesEmployes]
  • خطوط ۳۵–۴۲: تست متد [GetCotisations]
  • خطوط ۴۵–۵۳: تست متد [GetEmploye]
  • خطوط ۵۶–۶۵: آزمایش روش [GetEmploye] هنگام رخ دادن یک استثنا.

تولید پروژه، فایل‌های DLL و [pam-dao-spring-nhibernate.dll] را در پوشه [bin/Release] ایجاد می‌کند:

ما با استفاده از ابزار [NUnit-Gui] نسخهٔ 2.5، DLL و [pam-dao-spring-nhibernate.dll] را بارگذاری کرده و تست‌ها را اجرا می‌کنیم:

Image

آزمایش‌های بالا با موفقیت انجام شدند.

2.2.4. تولید of and DLL از لایه [dao]

پس از نوشتن و تست کلاس [PamDaoNHibernate]، ما DLL را از لایه [dao] به شرح زیر تولید خواهیم کرد:

  • [1]؛ برنامه‌های تست از ساخت پروژه مستثنی شده‌اند
  • [2,3]، پیکربندی پروژه
  • [4]، تولید پروژه
  • DLL در پوشه [bin/Release] تولید می‌شود [5].