Skip to content

13. برنامه [SimuPaie] – نسخه ۹ – یکپارچه‌سازی اسپرینگ / NHibernate

در اینجا، پیشنهاد می‌کنیم که برنامه سه‌لایه ASP.NET را از نسخه ۷ ([pam-v7-3tier-nhibernate-multivues-multipages]) تطبیق دهیم. معماری لایه‌ای این برنامه به شرح زیر بود:

در مثال فوق، لایه [dao] با استفاده از فریم‌ورک NHibernate پیاده‌سازی شده بود. چارچوب Spring تنها برای یکپارچه‌سازی لایه‌ها با یکدیگر استفاده شده بود. چارچوب Spring کلاس‌های کمکی برای کار با چارچوب NHibernate فراهم می‌کند. استفاده از این کلاس‌ها نوشتن کد لایه [dao] را ساده‌تر می‌سازد. معماری قبلی به شرح زیر تکامل می‌یابد:

به دلیل ساختار لایه‌ای مورد استفاده، پیاده‌سازی یکپارچه‌سازی Spring با NHibernate تنها منجر به تغییر در لایه [dao] می‌شود. لایه‌های [presentation] (وب / ASP.NET) و [metier] نیازی به تغییر نخواهند داشت. این مزیت اصلی معماری‌های لایه‌ای یکپارچه شده توسط Spring است.

در بخش بعدی، ما لایه [dao] را در کنار [Spring / NHibernate] با کامنت‌گذاری روی کد یک راه‌حل عملیاتی، پیاده‌سازی خواهیم کرد. ما تلاش نمی‌کنیم تا تمام گزینه‌های پیکربندی یا کاربردهای چارچوب [Spring / Nhibernate] را پوشش دهیم. خوانندگان می‌توانند با کمک مستندات Spring.NET و [http://www.springframework.net/documentation.html] (ژوئن ۲۰۱۰)، راه‌حل پیشنهادی را برای مشکلات خود تطبیق دهند.

رویکردی که برای ساخت لایه‌های [dao] و [metier] دنبال شده، رویکرد نسخه ۳ است، همانطور که در پاراگراف ۷ توصیف شده است. رویکردی که برای لایه [présentation] دنبال شده، رویکرد نسخه ۷ است، همانطور که در پاراگراف ۱۱ توصیف شده است.

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

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

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

  • در [1]، پروژه به طور کلی
    • پوشه [pam] شامل کلاس‌های پروژه و همچنین پیکربندی entities NHibernate است
    • فایل‌های [App.config] و [Dao.xml] فریم‌ورک Spring / NHibernate را پیکربندی می‌کنند. ما باید محتویات این دو فایل را شرح دهیم.
  • در [2]، کلاس‌های مختلف پروژه
    • در پوشه [entites]، انتیت‌های 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 برای SGBD 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] در پوشه [entites] [1] پروژه گردآوری شده‌اند. این اشیاء مشابه اشیاء پروژه [pam-dao-nhibernate] هستند، با یک تفاوت در فایل‌های پیکربندی 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] یافت خواهد شد

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>
    <!--بخش‌های پیکربندی -->
    <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 است، در این فایل یافت می‌شود.

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">

    <!-- اشاره شده توسط فایل پیکربندی اصلی زمینهٔ برنامه -->
    <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.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>

    <!-- مدیر تراکنش -->
    <object id="transactionManager"
        type="Spring.Data.NHibernate.HibernateTransactionManager, Spring.Data.NHibernate12">
        <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.NHibernate12 قرار دارد.
  • خط 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.NHibernate12 است. این مدیر باید پارامترهای اتصال به پایگاه داده (خط ۳۴) و همچنین SessionFactory از NHibernate (خط ۳۵) را بشناسد.
  • خطوط 39–43 ویژگی‌های کلاس HibernateTemplate را تعریف می‌کنند که آن نیز یک کلاس Spring است. این کلاس به عنوان یک کلاس ابزار در داخل کلاسی که لایه [dao] را پیاده‌سازی می‌کند، استفاده خواهد شد. این کلاس تعامل با اشیاء NHibernate را تسهیل می‌کند. این کلاس دارای ویژگی‌هایی است که باید مقداردهی اولیه شوند:
    • خط ۴۰: SessionFactory از NHibernate
    • خط ۴۱: ویژگی TemplateFlushMode حالت همگام‌سازی برای زمینه پایداری NHibernate با پایگاه داده را تعیین می‌کند. حالت Auto تضمین می‌کند که همگام‌سازی انجام خواهد شد:
      • در پایان یک تراکنش
      • قبل از یک عملیات SELECT
    • خط ۴۲: پرس‌وجوهای HQL (زبان پرس‌وجوی Hibernate) پنهان‌سازی (کش) خواهند شد. این ممکن است منجر به بهبود عملکرد شود.
  • خطوط ۴۶–۴۸ کلاس پیاده‌سازی برای لایه [dao] را تعریف می‌کنند
    • خط ۴۶: لایه [dao] توسط کلاس [PamdaoSpringNHibernate] از کلاس DLL [pam-dao-spring-nhibernate] پیاده‌سازی خواهد شد. پس از ایجاد نمونه از کلاس، متد init کلاس بلافاصله اجرا خواهد شد. هنگامی که کانtejner Spring بسته می‌شود، متد destroy کلاس اجرا خواهد شد.
    • خط ۴۷: کلاس [PamDaoSpringNHibernate] دارای یک خاصیت HibernateTemplate خواهد بود که با خاصیت HibernateTemplate از خط ۳۹ مقداردهی اولیه می‌شود.

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

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

رابط [IPamDao] همانند پروژه [pam-dao-nhibernate] است:


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). این anotation نیاز به مدیریت تراکنش در داخل متد را از بین می‌برد.
  • خط ۱۶: متد init بلافاصله پس از instantiate شدن کلاس توسط Spring اجرا می‌شود. خواهیم دید که هدف آن initialise کردن فیلدهای private در خطوط ۱۱ و ۱۲ است. این متد در داخل یک transaction (خط ۱۵) اجرا خواهد شد.
  • متدهای رابط [IPamDao] در خطوط 28، 35 و 40 پیاده‌سازی شده‌اند.
  • خطوط ۲۸–۳۰: متد [GetAllIdentitesEmployes] به سادگی ویژگی موجود در خط ۱۲ را که توسط متد init مقداردهی اولیه شده است، برمی‌گرداند.
  • خطوط ۴۰–۴۲: متد [GetCotisations] به‌سادگی ویژگی را از خط ۱۱ بازمی‌گرداند که توسط متد init مقداردهی اولیه شده بود.

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

13.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 کپی می‌شود.
  • خط ۱۴: ما فهرست تمام موجودیت‌های نوع Cotisations را درخواست می‌کنیم. می‌دانیم که این فهرست تنها شامل یک عنصر است. بنابراین عنصر اول فهرست را برای به‌دست‌آوردن نرخ‌های مشارکت بازیابی می‌کنیم.
  • خطوط ۷ و ۱۴ دو فیلد خصوصی کلاس را مقداردهی اولیه می‌کنند.

13.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 را بازیابی می‌کند
  • خط ۱۲: معمولاً اگر کارمند وجود داشته باشد، باید یک لیست شامل یک عنصر واحد بازگردانده شود
  • خط ۱۴: اگر اینطور نباشد، یک استثنا پرتاب می‌شود
  • خط ۱۶: اگر اینطور باشد، اولین کارمند لیست را بازمی‌گرداند

13.1.5.5. Conclusion

اگر کد در لایه [dao] را هنگام استفاده مقایسه کنیم

  1. NHibernate
  2. چارچوب Spring / NHibernate

می‌توانیم ببینیم که راه‌حل دوم منجر به کدی ساده‌تر شده است.

13.2. آزمون لایه [dao]

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

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

  • در [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 {
                // مثال‌سازی لایه [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] که در بخش 13.1.4.1 مورد بحث قرار گرفته است، پیکربندی می‌شوند.

اجرای برنامه با پایگاه‌داده توصیف‌شده در بخش 6.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]
  • خط ۵: نرخ‌های مشارکت

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 {
        //لایه [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);
        }
    }
}

این کلاس قبلاً در بخش 7.3.4 مورد بحث قرار گرفته است.

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

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

Image

همان‌طور که در بالا نشان داده شده است، آزمایش‌ها موفقیت‌آمیز بودند.

تمرین عملی:


  • تست‌ها را برای کلاس [PamDaoSpringNHibernate] روی ماشین پیاده‌سازی کنید.
  • از فایل‌های پیکربندی مختلف [Dao.xml] استفاده کنید تا از سایر SGBD (Firebird، MySQL، Postgres، SQL Server) استفاده کنید.

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

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

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

13.3. لایه کسب‌وکار

بیایید نگاهی دیگر به معماری کلی برنامه [SimuPaie] بیندازیم:

اکنون فرض می‌کنیم که لایه [dao] کامل شده و در داخل DLL و [pam-dao-spring-nhibernate.dll] قرار گرفته است. اکنون بر لایه [metier] تمرکز می‌کنیم. این لایه قوانین کسب‌وکار را پیاده‌سازی می‌کند، در این مورد قوانین محاسبه حقوق.

پروژه ویژوال استودیو برای لایه کسب‌وکار ممکن است چیزی شبیه به این باشد:

  • در [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 را از لایه [metier] تولید خواهیم کرد که آن را [pam-metier-dao-spring-nhibernate] نام‌گذاری می‌کنیم:

  • به [1]، تست NUnit با موفقیت انجام شد
  • به [2]، DLL تولیدشده توسط پروژه

ما DLL را از لایه [metier] به فایل‌های DLL که از قبل در پوشه [lib] موجود هستند اضافه خواهیم کرد [3]:

13.4. لایه [web]

بیایید نگاهی دیگر به معماری کلی برنامه [SimuPaie] بیندازیم:

ما فرض می‌کنیم که لایه‌های [dao] و [métier] کامل و در داخل لایه‌های DLL و [pam-dao-spring-nhibernate, pam-metier-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] و [metier] همان‌هایی هستند که ما همین‌اکنون ساخته‌ایم.

فایل [Dao.xml] همان فایلی است که در ساخت لایه [metier] استفاده می‌شود. فایل [Web.config] مربوط به [pam-v7] است، که پیکربندی بهار / پیکربندی NHibernate که در فایل‌های [App.config] لایه‌های [dao] و [metier] یافت شد. فایل [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>
    <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]

خطوط ۷–۱۱ و ۱۶–۲۳ حاوی پیکربندی Spring هستند که در فایل‌های [App.config] از لایه‌های [dao] و [metier] که قبلاً ایجاد شده‌اند، یافت می‌شوند، با یک تفاوت: در فایل‌های [App.config]، خط ۱۷ به صورت زیر نوشته شده بود:


      <resource uri="Dao.xml" />

با پیکربندی زیر:

فایل [Dao.xml] در پوشه [bin] داخل پوشه پروژه وب کپی می‌شود. با استفاده از سینتکس


      <resource uri="Dao.xml" />

فایل [Dao.xml] در پوشهٔ جاری فرایندی که برنامهٔ وب را اجرا می‌کند، جستجو خواهد شد. مشخص شد که این پوشه، پوشهٔ [bin] در داخل پوشهٔ پروژهٔ وب در حال اجرا نیست. شما باید بنویسید:


      <resource uri="~/Dao.xml" />

تا فایل [Dao.xml] در پوشه [bin] که در داخل پوشه پروژه وب در حال اجرا قرار دارد، جستجو شود.


سؤال: این برنامه وب را روی یک ماشین مستقر کنید.


13.5. Conclusion

ما از معماری زیر به معماری جدید منتقل شده‌ایم:

در معماری:

هدف پیاده‌سازی لایه [dao] با بهره‌گیری از قابلیت‌های ارائه‌شده توسط یکپارچه‌سازی NHibernate در Spring بود.

ما دریافتیم که این:

  • لایه [dao] را تحت تأثیر قرار داد. نوشتن آن ساده‌تر بود اما به پیکربندی پیچیده‌تری از Spring نیاز داشت.
  • تأثیر جزئی بر لایه‌های [metier] و [web] داشت

این یک نمونه دیگر از مزایای معماری لایه‌لایه را ارائه داد.