Skip to content

1. بخش ۱

نسخه PDF سند در |ICI| موجود است.

نمونه‌های موجود در سند در |ICI| در دسترس هستند.

1.1. Introduction

اهداف این مقاله:

  • نوشتن یک برنامه وب سه‌لایه [interface utilisateur, métier, accès aux données]
  • برای پیکربندی برنامه با استفاده از Spring IOC
  • برای نوشتن نسخه‌های مختلف با تغییر پیاده‌سازی یکی یا چند تا از سه لایه.

ابزارهای مورد استفاده:

  • Visual Studio.net برای توسعه – به بخش ۳.۱ در پیوست مراجعه کنید؛
  • سرور وب Cassini برای اجرا – به بخش ۳.۲ پیوست مراجعه کنید؛
  • Nunit برای تست واحد – به بخش 3.4 پیوست مراجعه کنید؛
  • Spring برای یکپارچه‌سازی و پیکربندی لایه‌های اپلیکیشن وب – به بخش 3.3 پیوست مراجعه کنید؛

در مقیاس مبتدی–میانی–پیشرفته، این سند در دسته [intermédiaire-avancé] قرار می‌گیرد. درک آن نیازمند پیش‌نیازهای مختلفی است. برخی از این پیش‌نیازها را می‌توان در اسنادی که من نوشته‌ام یافت. در مواردی که چنین است، به آن‌ها ارجاع می‌دهم. بدیهی است که این صرفاً یک پیشنهاد است و خواننده آزاد است از اسناد مورد علاقه خود استفاده کند.

این سند از ساختار سندی که برای جاوا [معماری‌های سه لایه و معماری‌های MVC با Struts، Spring و Java ] نوشته شده است، پیروی می‌کند. ما در حال ساخت اپلیکیشن وب سه‌لایه MVC هستیم که با استفاده از VB.NET به زبان جاوا نوشته شده است. نکتهٔ ما این است که پلتفرم‌های توسعهٔ جاوا و .NET به‌اندازهٔ کافی شبیه به هم هستند که مهارت‌های کسب‌شده در هر یک از این دو حوزه را بتوان در حوزه‌ی دیگر نیز به‌کار برد.

به نظر نمی‌رسد راه‌حل توسعهٔ MVC ASP.NET شناخته‌شده‌ای وجود داشته باشد. راه‌حل زیر از روشی که در سند [توسعه وب با ASP.NET 1.1 (2004)] معرفی شده است، پیروی می‌کند. اگرچه این روش از این مزیت برخوردار است که از مفاهیمی که در توسعه J2EE به طور گسترده رایج هستند، استفاده می‌کند، با این حال باید به همان شکلی که هست در نظر گرفته شود: c.a.d، تنها یکی از روش‌های متعدد توسعه MVC. به محض اینکه یک روش توسعه برای MVC در ASP.NET به طور گسترده پذیرفته شود، باید از آن روش استفاده کرد. نسخه .NET از Spring که در حال حاضر در دست توسعه است، می‌تواند یک راه‌حل اولیه باشد.

1.2. برنامهٔ webarticles

در اینجا اجزای یک اپلیکیشن وب تجارت الکترونیک ساده‌شده را ارائه می‌دهیم. این به کاربران وب امکان می‌دهد تا:

  • مشاهده فهرستی از اقلام موجود در پایگاه داده
  • تا برخی از آنها را به سبد خرید آنلاین اضافه کنند
  • تا انتخاب خود را تأیید کنند. این تأیید به سادگی سطوح موجودی اقلام خریداری شده را در پایگاه داده به‌روز می‌کند.

نماهای مختلفی که به کاربر ارائه می‌شوند به شرح زیر خواهد بود:

- نمای «LISTE» که فهرستی از اقلام قابل فروش را نمایش می‌دهد
- نمای [INFOS] که اطلاعات بیشتری در مورد یک محصول ارائه می‌دهد:
  • نماهای [PANIER] و [PANIERVIDE] که محتویات سبد خرید مشتری را نمایش می‌دهند
  • نمای [ERREURS] که هرگونه خطای برنامه را گزارش می‌کند

Image

1.3. معماری کلی برنامه

ما می‌خواهیم یک برنامه با معماری سه‌لایه زیر بسازیم:

  • سه لایه از طریق استفاده از رابط‌ها مستقل شده‌اند
  • یکپارچه‌سازی لایه‌های مختلف توسط Spring انجام می‌شود
  • به هر لایه فضای نام (namespace) مخصوص به خود اختصاص داده شده است: web (لایه UIdomain (لایه کسب‌وکار) و DAO (لایه دسترسی به داده).

برنامه از معماری MVC (مدل–نما–کنترل‌کننده) پیروی خواهد کرد. اگر به نمودار لایه‌ای بالا مراجعه کنیم، معماری MVC به شرح زیر در آن جای می‌گیرد:

پردازش یک درخواست مشتری مراحل زیر را دنبال می‌کند:

  1. کلاینت درخواستی را به کنترلر ارسال می‌کند. در این مورد، کنترلر یک صفحه .aspx است که یک نقش خاص به آن اختصاص داده شده است. این صفحه تمام درخواست‌های کلاینت را مدیریت می‌کند. این نقطه ورود به برنامه است. این «C» در ساختار MVC است.
  1. کنترل‌کننده این درخواست را پردازش می‌کند. برای این کار، ممکن است به کمک لایه کسب‌وکار، که در معماری MVC به‌عنوان مدل «M» شناخته می‌شود، نیاز داشته باشد.
  2. کنترل‌کننده پاسخی را از لایه کسب‌وکار دریافت می‌کند. درخواست مشتری پردازش شده است. این ممکن است به چندین پاسخ احتمالی منجر شود. یک مثال کلاسیک این است
    • یک صفحهٔ خطا اگر درخواست نتوانست به درستی پردازش شود
    • در غیر این صورت، یک صفحه تأیید
  3. کنترل‌کننده پاسخ (= نما) را برای ارسال به کلاینت انتخاب می‌کند. این معمولاً صفحه‌ای حاوی عناصر پویا است. کنترل‌کننده این عناصر را در اختیار نما قرار می‌دهد.
  4. ویو به کلاینت ارسال می‌شود. این «V» در ساختار MVC است.

1.4. مدل

در اینجا به M در MVC می‌پردازیم. مدل شامل عناصر زیر است:

  1. کلاس‌های کسب‌وکار
  2. کلاس‌های دسترسی به داده
  3. پایگاه داده

1.4.1. پایگاه داده

پایگاه داده تنها شامل یک جدول به نام ARTICLES است. این جدول با استفاده از دستورات زیر SQL ایجاد شده است:

CREATE TABLE ARTICLES (
    ID            INTEGER NOT NULL,
    NOM           VARCHAR(20) NOT NULL,
    PRIX          NUMERIC(15,2) NOT NULL,
    STOCKACTUEL   INTEGER NOT NULL,
    STOCKMINIMUM  INTEGER NOT NULL
);
/* محدودیت‌ها */
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_ID check (ID>0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_PRIX check (PRIX>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKACTUEL check (STOCKACTUEL>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_STOCKMINIMUM check (STOCKMINIMUM>=0);
ALTER TABLE ARTICLES ADD CONSTRAINT CHK_NOM check (NOM<>'');
ALTER TABLE ARTICLES ADD CONSTRAINT UNQ_NOM UNIQUE (NOM);
/* کلید اصلی */
ALTER TABLE ARTICLES ADD CONSTRAINT PK_ARTICLES PRIMARY KEY (ID);
id
کلید اصلی که یک آیتم را به‌طور منحصربه‌فرد شناسایی می‌کند
nom
نام آیتم
prix
قیمت آن
stockactuel
موجودی فعلی
stockminimum
سطح موجودی که در زیر آن باید سفارش مجدد ثبت شود

1.4.2. فضای نام‌های مدل

مدل M در اینجا به صورت دو فضای نام ارائه شده است:

  • istia.st.articles.dao: شامل کلاس‌های دسترسی به داده‌ها برای لایه [dao] است
  • istia.st.articles.domain: شامل کلاس‌های کسب‌وکار لایه [domain] است

هر یک از این فضاهای نام در فایل «assembly» مخصوص به خود تولید خواهند شد:

assembly
contenu
rôle
webarticles-dao
- [IArticlesDao]: رابط دسترسی به لایه [dao].
این تنها رابط قابل مشاهده برای لایه [domain] است. این لایه رابط‌های دیگری را نمی‌بیند.
- [Article]: کلاسی که یک آیتم را تعریف می‌کند
- [ArticlesDaoArrayList]: کلاس پیاده‌سازی برای
رابط [IArticlesDao] با [ArrayList]
لایه دسترسی به داده
- به طور کامل در داخل
[dao] از معماری سه‌لایه
برنامه وب
webarticles-domain
- [IArticlesDomain]: رابطی که دسترسی به لایه [domain] را فراهم می‌کند. این تنها رابطی است که برای لایه وب قابل مشاهده است. این لایه رابط‌های دیگری را نمی‌بیند.
- [AchatsArticles]: کلاسی که [IArticlesDomain] را پیاده‌سازی می‌کند
- [Achat]: کلاسی که نمایانگر خرید مشتری است
- [Panier]: کلاسی که تمام خریدهای یک مشتری را نشان می‌دهد
نماینده مدل خریدهای آنلاین است
وب – کاملاً در داخل قرار دارد
لایه [domain] از معماری
اپلیکیشن وب سه‌لایه

1.4.3. لایه [dao]

لایه [dao] شامل عناصر زیر است:

  • [IArticlesDao]: رابط دسترسی به لایه [dao]

  • [Article]: کلاسی که یک آیتم را تعریف می‌کند

  • [ArticlesDaoArrayList]: یک کلاس پیاده‌سازی برای رابط [IArticlesDao]، با استفاده از کلاس [ArrayList]

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

Image

توضیحات:

  • پروژه [dao] از نوع [bibliothèque de classes] است
  • کلاس‌ها در یک ساختار درختی قرار گرفته‌اند که پوشه [istia] به‌عنوان ریشه است. همگی در فضای نام [istia.st.articles.dao] قرار دارند.

1.4.3.1. کلاس [Article]

کلاس تعریف‌کننده یک مقاله به شرح زیر است:

Imports System

Namespace istia.st.articles.dao

    Public Class Article

         ' fields خصوصی
        Private _id As Integer
        Private _nom As String
        Private _prix As Double
        Private _stockactuel As Integer
        Private _stockminimum As Integer

         'شناسه آیتم
        Public Property id() As Integer
            Get
                Return _id
            End Get
            Set(ByVal Value As Integer)
                If Value <= 0 Then
                    Throw New Exception("Le champ id [" + Value.ToString + "] est invalide")
                End If
                Me._id = Value
            End Set
        End Property

         ' نام آیتم
        Public Property nom() As String
            Get
                Return _nom
            End Get
            Set(ByVal Value As String)
                If Value Is Nothing OrElse Value.Trim.Equals("") Then
                    Throw New Exception("Le champ nom [" + Value + "] est invalide")
                End If
                Me._nom = Value
            End Set
        End Property

         ' قیمت کالا
        Public Property prix() As Double
            Get
                Return _prix
            End Get
            Set(ByVal Value As Double)
                If Value < 0 Then
                    Throw New Exception("Le champ prix [" + Value.ToString + "] est invalide")
                End If
                Me._prix = Value
            End Set
        End Property

         ' موجودی فعلی کالا
        Public Property stockactuel() As Integer
            Get
                Return _stockactuel
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Le champ stockActuel [" + Value.ToString + "] est invalide")
                End If
                Me._stockactuel = Value
            End Set
        End Property

         'حداقل موجودی برای کالا
        Public Property stockminimum() As Integer
            Get
                Return _stockminimum
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Le champ stockMinimum [" + Value.ToString + "] est invalide")
                End If
                Me._stockminimum = Value
            End Set
        End Property

         ' تولیدکننده پیش‌فرض
        Public Sub New()
        End Sub

         ' تولیدکننده دارای ویژگی‌ها
        Public Sub New(ByVal id As Integer, ByVal nom As String, ByVal prix As Double, ByVal stockactuel As Integer, ByVal stockminimum As Integer)
            Me.id = id
            Me.nom = nom
            Me.prix = prix
            Me.stockactuel = stockactuel
            Me.stockminimum = stockminimum
        End Sub

         ' روش شناسایی آیتم
        Public Overrides Function ToString() As String
            Return "[" + id.ToString + "," + nom + "," + prix.ToString + "," + stockactuel.ToString + "," + stockminimum.ToString + "]"
        End Function
    End Class
End Namespace

این کلاس فراهم می‌کند:

  1. یک سازنده برای تنظیم پنج بخش اطلاعات یک آیتم: [id, nom, prix, stockactuel, stockminimum]
  2. ویژگی‌های عمومی برای خواندن و نوشتن این پنج مورد اطلاعات.
  3. اعتبارسنجی داده‌های واردشده برای آیتم. اگر داده‌ها نادرست باشند، یک استثنا پرتاب می‌شود.
  4. یک متد، toString، که مقدار یک آیتم را به صورت یک رشته بازیابی می‌کند. این امر اغلب برای اشکال‌زدایی یک برنامه کاربردی مفید است.

1.4.3.2. رابط [IArticlesDao]

رابط [IArticlesDao] به شرح زیر تعریف شده است:

Imports System
Imports System.Collections

Namespace istia.st.articles.dao

    Public Interface IArticlesDao
         ' فهرست تمام اقلام
        Function getAllArticles() As IList
         ' یک آیتم اضافه می‌کند
        Function ajouteArticle(ByVal unArticle As Article) As Integer
         ' یک آیتم را حذف می‌کند
        Function supprimeArticle(ByVal idArticle As Integer) As Integer
         ' ویرایش یک آیتم
        Function modifieArticle(ByVal unArticle As Article) As Integer
         'جستجوی یک آیتم
        Function getArticleById(ByVal idArticle As Integer) As Article
         ' تمام اقلام را حذف می‌کند
        Sub clearAllArticles()
         'سطح موجودی یک آیتم را تغییر می‌دهد
        Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer
    End Interface
End Namespace

نقش روش‌های مختلف در رابط کاربری به شرح زیر است:

getAllArticles
بازگرداندن تمام رکوردها از منبع داده
clearAllArticles
منبع داده را پاک می‌کند
getArticleById
شیء [Article] را که با کلید اصلی‌اش شناسایی شده است بازمی‌گرداند
ajouteArticle
امکان افزودن یک آیتم به منبع داده را فراهم می‌کند
modifieArticle
به شما امکان می‌دهد یک آیتم را در منبع داده اصلاح کنید
supprimerArticle
به شما امکان می‌دهد یک آیتم را از منبع داده حذف کنید
changerStockArticle
به شما امکان می‌دهد سطح موجودی یک آیتم را در منبع داده تغییر دهید

رابط، برنامه‌های کلاینت را با تعدادی متد فراهم می‌کند که تنها بر اساس امضاهایشان تعریف شده‌اند. این رابط به نحوه پیاده‌سازی واقعی این متدها کاری ندارد. این امر انعطاف‌پذیری را در یک برنامه فراهم می‌کند. برنامه کلاینت به جای یک پیاده‌سازی خاص از آن رابط، به خود رابط فراخوانی می‌کند.

انتخاب یک پیاده‌سازی خاص از طریق یک فایل پیکربندی Spring انجام خواهد شد. برای نشان دادن اینکه هنگام تست اپلیکیشن وب، تنها رابط دسترسی به داده اهمیت دارد—و نه کلاس پیاده‌سازی آن—ابتدا منبع داده را با استفاده از یک شیء ساده [ArrayList] پیاده‌سازی خواهیم کرد. سپس، راه‌حلی مبتنی بر SGBD ارائه خواهیم داد.

1.4.3.3. کلاس پیاده‌سازی [ArticlesDaoArrayList]

کلاس پیاده‌سازی [ArticlesDaoArrayList] به صورت زیر تعریف شده است:

Imports System
Imports System.Collections

Namespace istia.st.articles.dao

    Public Class ArticlesDaoArrayList
        Implements istia.st.articles.dao.IArticlesDao

        Private articles As New ArrayList
        Private Const nbArticles As Integer = 4

         'تولیدکننده پیش‌فرض
        Public Sub New()
             'تولید برخی اقلام
            For i As Integer = 1 To nbArticles
                articles.Add(New Article(i, "article" + i.ToString, i * 10, i * 10, i * 10))
            Next
        End Sub

         'فهرست تمام اقلام
        Public Function getAllArticles() As IList Implements IArticlesDao.getAllArticles
             ' فهرست اقلام را بازمی‌گرداند
            SyncLock Me
                Return articles
            End SyncLock
        End Function

         ' حذف همه اقلام
        Public Sub clearAllArticles() Implements IArticlesDao.clearAllArticles
             ' پاک کردن فهرست آیتم‌ها
            SyncLock Me
                articles.Clear()
            End SyncLock
        End Sub

         'بازیابی یک آیتم با شناسهٔ کلید آن
        Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDao.getArticleById
             'جستجوی آیتم در لیست
            SyncLock Me
                Dim ipos As Integer = posArticle(articles, idArticle)
                If ipos <> -1 Then
                    Return CType(articles(ipos), Article)
                Else
                    Return Nothing
                End If
            End SyncLock
        End Function

         ' افزودن یک آیتم به فهرست آیتم‌ها
        Public Function ajouteArticle(ByVal unArticle As Article) As Integer Implements IArticlesDao.ajouteArticle
             ' مورد به فهرست موارد اضافه می‌شود
            SyncLock Me
                 'بررسی می‌کند که قبلاً وجود نداشته باشد
                Dim ipos As Integer = posArticle(articles, unArticle.id)
                If ipos <> -1 Then
                    Throw New Exception("L'article d'id [" + unArticle.id.ToString + "] existe déjà")
                End If
                 'آیتم اضافه می‌شود
                articles.Add(unArticle)
                 ' بازگرداندن نتیجه
                Return 1
            End SyncLock
        End Function

         ' ویرایش یک آیتم
        Public Function modifieArticle(ByVal articleNouveau As Article) As Integer Implements IArticlesDao.modifieArticle
             ' ویرایش یک آیتم
            SyncLock Me
                 'بررسی می‌کند که وجود دارد
                Dim ipos As Integer = posArticle(articles, articleNouveau.id)
                 ' اگر وجود نداشته باشد
                If ipos = -1 Then Return 0
                 ' وجود دارد – آن را ویرایش می‌کنیم
                articles(ipos) = articleNouveau
                 ' نتیجه را بازگردانید
                Return 1
            End SyncLock
        End Function

         ' حذف یک آیتم با شناسهٔ کلیدش
        Public Function supprimeArticle(ByVal idArticle As Integer) As Integer Implements IArticlesDao.supprimeArticle
             ' حذف یک آیتم
            SyncLock Me
                 'بررسی وجود آن
                Dim ipos As Integer = posArticle(articles, idArticle)
                 ' اگر وجود نداشته باشد
                If ipos = -1 Then Return 0
                 ' وجود دارد – آن را حذف کنید
                articles.RemoveAt(ipos)
                 ' نتیجه را بازمی‌گرداند
                Return 1
            End SyncLock
        End Function

         'تغییر سطح موجودی یک آیتم مشخص‌شده با کلید آن
        Public Function changerStockArticle(ByVal idArticle As Integer, ByVal mouvement As Integer) As Integer Implements IArticlesDao.changerStockArticle
             'تغییر سطح موجودی یک کالا
            SyncLock Me
                 'بررسی می‌کند که آیا وجود دارد
                Dim ipos As Integer = posArticle(articles, idArticle)
                 ' اگر وجود نداشته باشد
                If ipos = -1 Then Return 0
                 ' اگر موجود باشد – در صورت امکان موجودی آن را به‌روزرسانی کنید
                Dim unArticle As Article = CType(articles(ipos), Article)
                 ' فقط در صورتی موجودی را تغییر می‌دهیم که موجودی کافی باشد
                If unArticle.stockactuel + mouvement >= 0 Then
                    unArticle.stockactuel += mouvement
                    Return 1
                Else
                    Return 0
                End If
            End SyncLock
        End Function

         'جستجوی یک آیتم با استفاده از کلید آن
        Private Function posArticle(ByVal listArticles As ArrayList, ByVal idArticle As Integer) As Integer
             'موقعیت آیتم [idArticle] را در لیست بازمی‌گرداند، یا در صورت عدم یافتن -1
            Dim unArticle As Article
            For i As Integer = 0 To listArticles.Count - 1
                unArticle = CType(listArticles(i), Article)
                If unArticle.id = idArticle Then
                    Return i
                End If
            Next
             ' یافت نشد
            Return -1
        End Function
    End Class

End Namespace

توضیحات:

  • منبع داده توسط فیلد خصوصی [articles] از نوع [ArrayList] شبیه‌سازی می‌شود.
  • سازنده کلاس به‌طور پیش‌فرض ۴ آیتم در منبع داده ایجاد می‌کند.
  • تمام روش‌های دسترسی به داده‌ها همگام‌سازی شده‌اند تا از بروز مشکلات ناشی از دسترسی همزمان به منبع داده جلوگیری شود. در هر لحظه، تنها یک نخ به یک روش مشخص دسترسی دارد.
  • متد [posArticle] برای تعیین موقعیت [0..N] در منبع [ArrayList] یک آیتم که با شماره آن شناسایی شده است، استفاده می‌شود. اگر آیتم وجود نداشته باشد، متد موقعیت -1 را برمی‌گرداند. این متد توسط متدهای دیگر به طور مکرر فراخوانی می‌شود.
  • روش [ajouteArticle] برای افزودن یک آیتم به فهرست آیتم‌ها استفاده می‌شود. این روش تعداد آیتم‌های درج‌شده را برمی‌گرداند: 1 اگر آیتم وجود داشته باشد، در غیر این صورت 0. اگر آیتم از قبل وجود داشته باشد، یک استثنا پرتاب می‌شود.
  • روش [modifieArticle] برای اصلاح یک آیتم موجود استفاده می‌شود. این روش تعداد آیتم‌های اصلاح‌شده را برمی‌گرداند: 1 اگر آیتم وجود داشته باشد، در غیر این صورت 0.
  • روش [supprimeArticle] برای حذف یک آیتم موجود استفاده می‌شود. این روش تعداد آیتم‌های حذف‌شده را برمی‌گرداند: 1 اگر آیتم وجود داشته باشد، در غیر این صورت 0.
  • روش [getAllArticles] فهرستی از تمام آیتم‌ها را بازمی‌گرداند
  • روش [getArticleById] برای بازیابی یک آیتم با شمارهٔ آن استفاده می‌شود. مقدار [nothing] در صورتی که آیتم وجود نداشته باشد، بازگردانده می‌شود.
  • این کد هیچ دشواری واقعی ندارد. خواندن و درک آن را به خواننده واگذار می‌کنیم.

1.4.3.4. تولید اسمبلی لایه [dao]

پروژه Visual Studio برای تولید اسمبلی [webarticles-dao.dll] پیکربندی شده است. این اسمبلی در پوشه [bin] پروژه تولید می‌شود:

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

در جاوا، کلاس‌ها با استفاده از چارچوب [Junit] تست می‌شوند. در .NET، چارچوب Nunit همان قابلیت‌های تست واحد را ارائه می‌دهد:

Image

ساختار پروژه تست Visual Studio به شرح زیر است:

Image

توضیحات:

  • پروژه [tests] از نوع [bibliothèque de classes] است
  • تست‌های [NUnit] به یک مرجع به اسمبلی [nunit.framework.dll] نیاز دارند
  • کلاس تست [NUnit] یک نمونه از شیء تحت تست را از طریق Spring بازیابی می‌کند. بنابراین،
    • در پوشه [bin]، فایل‌های کلاس Spring
    • در [References]، مرجعی به اسمبلی [Spring-Core.dll] از پوشه [bin]
    • در [bin]، یک فایل پیکربندی برای Spring
  • کلاس تست به اسمبلی [webarticles-dao.dll] از لایه [dao] نیاز دارد. این اسمبلی در پوشه [bin] قرار داده شده و ارجاع آن به ارجاعات پروژه اضافه شده است.

کلاس تست [NUnit] نیازمند دسترسی به کلاس‌های در فضای نام [NUnit.Framework] است. بنابراین شامل دستور import زیر است:

Imports NUnit.Framework

فضا نام [NUnit.Framework] در «اسمبلی» [nunit.framework.dll] قرار دارد که باید به مراجع پروژه اضافه شود:

اگر [Nunit] نصب شده باشد، assembly [nunit.framework.dll] باید در لیست ارائه‌شده ظاهر شود. برای افزودن آن به پروژه، کافی است روی assembly دوبار کلیک کنید:

Image

کلاس تست [NUnit] برای لایه [dao] می‌تواند به صورت زیر باشد:

Imports System
Imports System.Collections
Imports NUnit.Framework
Imports istia.st.articles.dao
Imports System.Threading
Imports Spring.Objects.Factory.Xml
Imports System.IO

Namespace istia.st.articles.tests

    <TestFixture()> _
    Public Class NunitTestArticlesArrayList
         ' ابجکت مورد آزمایش
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             ' یک نمونه از سازندهٔ شیء Spring را بازیابی می‌کند
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
             ' درخواست نمونه‌سازی شیء articlesdao
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
        End Sub

        <Test()> _
        Public Sub testGetAllArticles()
             ' بررسی بصری
            listArticles()
        End Sub

        <Test()> _
        Public Sub testClearAllArticles()
             ' حذف همه مقالات
            articlesDao.clearAllArticles()
             ' بازیابی تمام مقالات
            Dim articles As IList = articlesDao.getAllArticles
             'بررسی: باید 0 باشد
            Assert.AreEqual(0, articles.Count)
        End Sub

        <Test()> _
        Public Sub testAjouteArticle()
             ' حذف همه آیتم‌ها
            articlesDao.clearAllArticles()
             ' بررسی: جدول آیتم‌ها باید خالی باشد
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' افزودن دو آیتم
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             'بررسی: باید دو آیتم وجود داشته باشد
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' بررسی بصری
            listArticles()
        End Sub

        <Test()> _
        Public Sub testSupprimeArticle()
             ' حذف همه آیتم‌ها
            articlesDao.clearAllArticles()
             'بررسی: جدول آیتم‌ها باید خالی باشد
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             'دو آیتم اضافه می‌شوند
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             'بررسی: باید ۲ آیتم وجود داشته باشد
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' آیتم ۴ حذف شد
            articlesDao.supprimeArticle(4)
             ' بررسی: باید یک آیتم باقی بماند
            articles = articlesDao.getAllArticles
            Assert.AreEqual(1, articles.Count)
             ' بررسی بصری
            listArticles()
        End Sub

        <Test()> _
        Public Sub testModifieArticle()
             ' تمام موارد حذف شدند
            articlesDao.clearAllArticles()
             'بررسی
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' افزودن ۲ مورد
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             'بررسی
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' جستجوی آیتم ۳
            Dim unArticle As Article = articlesDao.getArticleById(3)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article3")
             ' جستجو برای مورد ۴
            unArticle = articlesDao.getArticleById(4)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article4")
             ' اصلاحیه ماده ۴
            articlesDao.modifieArticle(New Article(4, "article4", 44, 44, 44))
             ' تأیید
            unArticle = articlesDao.getArticleById(4)
            Assert.AreEqual(unArticle.prix, 44, 0.000001)
             ' بررسی بصری
            listArticles()
        End Sub

        <Test()> _
        Public Sub testGetArticleById()
             ' حذف اقلام
            articlesDao.clearAllArticles()
             ' تأیید
            Dim articles As IList = articlesDao.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' افزودن ۲ مورد
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' تأیید
            articles = articlesDao.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' جستجوی آیتم ۳
            Dim unArticle As Article = articlesDao.getArticleById(3)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article3")
             ' جستجو برای آیتم ۴
            unArticle = articlesDao.getArticleById(4)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article4")
        End Sub

         'فهرست صفحه‌ای
        Private Sub listArticles()
            Dim articles As IList = articlesDao.getAllArticles
            For i As Integer = 0 To articles.Count - 1
                Console.WriteLine(CType(articles(i), Article).ToString)
            Next
        End Sub

        <Test()> _
        Public Sub testArticleAbsent()
             ' حذف همه آیتم‌ها
            articlesDao.clearAllArticles()
             ' جستجوی آیتم ۱
            Dim article As article = articlesDao.getArticleById(1)
             ' تأیید
            Assert.IsNull(article)
             ' ویرایش یک آیتم موجود نیست
            Dim i As Integer = articlesDao.modifieArticle(New article(1, "1", 1, 1, 1))
             ' هیچ ردیفی اصلاح نشد
            Assert.AreEqual(i, 0)
             ' حذف یک آیتم موجود نیست
            i = articlesDao.supprimeArticle(1)
             ' لازم نبود هیچ ردیفی را حذف کند
            Assert.AreEqual(0, i)
        End Sub

        <Test()> _
        Public Sub testChangerStockArticle()
             ' حذف تمام آیتم‌ها
            articlesDao.clearAllArticles()
             ' یک آیتم اضافه شده است
            Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
            Assert.AreEqual(nbArticles, 1)
             ' یک مقاله اضافه شد
            nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
            Assert.AreEqual(nbArticles, 1)
             ' ایجاد ۱۰۰ تاپیک
            Dim taches(99) As Thread
            For i As Integer = 0 To taches.Length - 1
                 ' موضوع i را ایجاد می‌کند
                taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
                 ' تنظیم نام تاپیک
                taches(i).Name = "tache_" & i
                 ' اجرای نخ i
                taches(i).Start()
            Next
             ' انتظار تا پایان همهٔ نخ‌ها
            For i As Integer = 0 To taches.Length - 1
                taches(i).Join()
            Next
             'بررسی‌ها – آیتم ۳ باید سطح موجودی ۱ داشته باشد
            Dim unArticle As Article = articlesDao.getArticleById(3)
            Assert.AreEqual(unArticle.nom, "article3")
            Assert.AreEqual(1, unArticle.stockactuel)
             ' کاهش موجودی برای آیتم ۴
            Dim erreur As Boolean = False
            Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
             'بررسی: موجودی آن نباید تغییر کرده باشد
            Assert.AreEqual(0, nbLignes)
             ' بازرسی چشمی
            listArticles()
        End Sub

        Public Sub décrémente()
             ' رشته آغاز شد
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
             ' کاهش موجودی در رشته
            articlesDao.changerStockArticle(3, -1)
             ' رشته کامل شد
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
        End Sub

    End Class
End Namespace

توضیحات:

  • ما می‌خواستیم یک برنامهٔ تست برای رابط [IArticlesDao] بنویسیم که مستقل از کلاس پیاده‌سازی آن باشد. بنابراین از Spring استفاده کردیم تا نام کلاس پیاده‌سازی را از برنامهٔ تست پنهان کنیم.
  • متد ویژگی <Setup()> یک مرجع به شی [articlesdao] که قرار است تست شود را از Spring بازیابی می‌کند. این در فایل زیر [spring-config.xml] تعریف شده است:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">

<objects>
    <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao"/>
</objects>

این فایل نام کلاس پیاده‌سازی [istia.st.articles.dao.ArticlesDaoArrayList] را برای رابط [IArticlesDao] و محل یافتن آن را مشخص می‌کند: [ webarticles-dao.dll]. از آنجایی که نمونه‌سازی به هیچ پارامتری نیاز ندارد، هیچ‌کدام در اینجا تعریف نشده‌اند.

  • اکثر تست‌ها به‌سادگی قابل درک هستند. خواننده تشویق می‌شود که نظرات را بخواند.
  • متد [testChangerStockArticle] نیاز به توضیح دارد. این متد ۱۰۰ نخ ایجاد می‌کند که وظیفه کاهش موجودی یک کالا را بر عهده دارند.
        <Test()> _
        Public Sub testChangerStockArticle()
             ' حذف همه آیتم‌ها
            articlesDao.clearAllArticles()
             ' افزودن یک آیتم
            Dim nbArticles As Integer = articlesDao.ajouteArticle(New Article(3, "article3", 30, 101, 3))
            Assert.AreEqual(nbArticles, 1)
             ' افزودن یک آیتم
            nbArticles = articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
            Assert.AreEqual(nbArticles, 1)
             ' ایجاد ۱۰۰ نخ
            Dim taches(99) As Thread
            For i As Integer = 0 To taches.Length - 1
                 ' ایجاد تاپیک i
                taches(i) = New Thread(New ThreadStart(AddressOf décrémente))
                 ' تنظیم نام نخ
                taches(i).Name = "tache_" & i
                 ' اجرای نخ i
                taches(i).Start()
            Next
             ' منتظر پایان همهٔ نخ‌ها بمان
            For i As Integer = 0 To taches.Length - 1
                taches(i).Join()
            Next
             'بررسی‌ها – آیتم ۳ باید سطح موجودی ۱ داشته باشد
            Dim unArticle As Article = articlesDao.getArticleById(3)
            Assert.AreEqual(unArticle.nom, "article3")
            Assert.AreEqual(1, unArticle.stockactuel)
             ' کاهش موجودی برای آیتم ۴
            Dim erreur As Boolean = False
            Dim nbLignes As Integer = articlesDao.changerStockArticle(4, -100)
             'بررسی: موجودی آن نباید تغییر کرده باشد
            Assert.AreEqual(0, nbLignes)
             ' بررسی بصری
            listArticles()
        End Sub

هدف در اینجا آزمایش دسترسی همزمان به منبع داده است. متد مسئول به‌روزرسانی موجودی به شرح زیر است:

        Public Sub décrémente()
             ' رشته راه‌اندازی شد
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " lancé")
             ' کاهش موجودی در رشته
            articlesDao.changerStockArticle(3, -1)
             ' رشته کامل شد
            System.Console.Out.WriteLine(Thread.CurrentThread.Name + " terminé")
        End Sub

مقدار موجودی کالا شمارهٔ ۳ را به اندازهٔ یک واحد کاهش می‌دهد. اگر به کد روش [testChangerStockArticle] مراجعه کنیم، می‌توانیم ببینیم که:

  • موجودی قلم شماره ۳ به مقدار اولیه ۱۰۱ مقداردهی شده است
  • ۱۰۰ نخ هر کدام این موجودی را به اندازه یک واحد کاهش می‌دهند
  • بنابراین، پس از اتمام اجرای تمام رشته‌ها، باید سطح موجودی برابر با ۱ باشد.

علاوه بر این، همچنان در همین متد، تلاشی برای منفی کردن موجودی کالا شماره ۴ انجام می‌شود. این کار باید ناموفق باشد.

برای آزمایش لایه [dao]، ما فایل‌های DLL و [tests-webarticles-dao.dll] را در پوشه [bin] از پروژه [tests] ایجاد می‌کنیم:

سپس، با استفاده از برنامه [Nunit-Gui]، این DLL را بارگذاری کرده و تست‌ها را اجرا می‌کنیم:

Image

در پنجره سمت چپ، می‌توانیم فهرست متدهای آزمایش‌شده را مشاهده کنیم. رنگ نقطه‌ای که قبل از نام هر متد قرار دارد، نشان می‌دهد که آیا متد با موفقیت (سبز) یا با شکست (قرمز) مواجه شده است. خوانندگانی که این سند را روی صفحه مشاهده می‌کنند، خواهند دید که همه تست‌ها با موفقیت پشت سر گذاشته شده‌اند. بنابراین فرض می‌کنیم که یک لایه [dao] عملیاتی در اختیار داریم.

1.4.4. لایه [domain]

لایه [domain] شامل عناصر زیر است:

  • [IArticlesDomain]: رابط دسترسی به لایه [domain]

  • [Achat]: کلاسی که یک خرید را تعریف می‌کند

  • [Panier]: کلاسی که یک سبد خرید را تعریف می‌کند

  • [AchatsArticles]: کلاسی که رابط [IArticlesDomain] را پیاده‌سازی می‌کند

ساختار راه‌حل [Visual Studio] در لایه [domain] به شرح زیر است:

Image

توضیحات:

  • پروژه [domain] از نوع [bibliothèque de classes] است
  • کلاس‌ها در یک ساختار درختی با پوشه [istia] به‌عنوان ریشه قرار گرفته‌اند. همگی در فضای نام [istia.st.articles.domain] هستند.
  • رابط DLL از لایه [dao] در پوشه [bin] پروژه جدید قرار داده شده است. علاوه بر این، این DLL به عنوان یک مرجع به پروژه اضافه شده است.

1.4.4.1. رابط [IArticlesDomain]

رابط [IArticlesDomain] لایه [métier] را از لایه [web] مستقل می‌کند. این مورد دوم از طریق این رابط به لایه [métier/domain] دسترسی پیدا می‌کند، بدون اینکه نیازی باشد خود را درگیر کلاسی کند که در واقع آن را پیاده‌سازی می‌کند. این رابط عملیات زیر را برای دسترسی به لایه کسب‌وکار تعریف می‌کند:

Imports Article = istia.st.articles.dao.Article

Namespace istia.st.articles.domain
    Public Interface IArticlesDomain
         ' روش‌ها
        Sub acheter(ByVal panier As Panier)
        Function getAllArticles() As IList
        Function getArticleById(ByVal idArticle As Integer) As Article
        ReadOnly Property erreurs() As ArrayList
    End Interface
End Namespace
Function getAllArticles() As IList
فهرست اشیاء [Article] را از منبع دادهٔ مرتبط بازمی‌گرداند
Function getArticleById(ByVal idArticle As Integer) As Article
شیء [Article] را که توسط [idArticle] شناسایی شده است بازمی‌گرداند
acheter(ByVal panier As Panier)
سبد خرید مشتری را با کاهش موجودی اقلام خریداری‌شده به میزان تعداد خریداری‌شده اعتبارسنجی می‌کند – در صورت ناکافی بودن موجودی ممکن است با شکست مواجه شود
ReadOnly Property erreurs() As ArrayList
فهرستی از هرگونه خطاهای رخ‌داده را بازمی‌گرداند – در صورت عدم وجود خطا خالی است

1.4.4.2. کلاس [Achat]

کلاس [Achat] یک خرید مشتری را نشان می‌دهد:

Imports istia.st.articles.dao

Namespace istia.st.articles.domain

    Public Class Achat

         ' fields خصوصی
        Private _article As article
        Private _qte As Integer

         ' سازندهٔ پیش‌فرض
        Public Sub New()
        End Sub

         ' سازنده با پارامترها
        Public Sub New(ByVal unArticle As article, ByVal qte As Integer)
             ' دسترسی از طریق ویژگی‌ها
            Me.article = unArticle
            Me.qte = qte
        End Sub

         ' مورد خریداری‌شده
        Public Property article() As article
            Get
                Return _article
            End Get
            Set(ByVal Value As article)
                _article = Value
            End Set
        End Property

         ' مقدار خریداری‌شده
        Public Property qte() As Integer
            Get
                Return _qte
            End Get
            Set(ByVal Value As Integer)
                If Value < 0 Then
                    Throw New Exception("Quantité [" + Value.ToString + "] invalide")
                End If
                _qte = Value
            End Set
        End Property

         ' مجموع خرید
        Public ReadOnly Property totalAchat() As Double
            Get
                Return _qte * _article.prix
            End Get
        End Property

         'شناسه
        Public Overrides Function ToString() As String
            Return "[" + _article.ToString + "," + _qte.ToString + "]"
        End Function
    End Class

End Namespace

نظرات:

  • کلاس [Achat] دارای ویژگی‌ها و متدهای زیر است:
Public Property article() As article
مورد خریداری‌شده
Public Property qte() As Integer
مقدار خریداری‌شده
Public ReadOnly Property totalAchat() As Double
مبلغ خرید
Public Overrides Function ToString() As String
رشته شناسه شیء
  • این کلاس دارای یک سازنده است که امکان مقداردهی اولیه ویژگی‌های [article, qte] را که خرید را تعریف می‌کنند، فراهم می‌آورد.

1.4.4.3. کلاس [Panier]

کلاس [Panier] تمام خریدهای مشتری را نشان می‌دهد:

Namespace istia.st.articles.domain

Public Class Panier

     ' میدان‌های خصوصی
    Private _achats As New ArrayList
        Private _totalPanier As Double = 0

     ' تولیدکننده پیش‌فرض
    Public Sub New()
    End Sub

     ' فهرست خرید
    Public ReadOnly Property achats() As ArrayList
        Get
            Return _achats
        End Get
    End Property

     ' مجموع خریدها
    Public ReadOnly Property totalPanier() As Double
        Get
            Return _totalPanier
        End Get
    End Property

     ' روش‌ها
    Public Sub ajouter(ByVal unAchat As Achat)
         'بررسی می‌کند که آیا خرید قبلاً وجود دارد یا خیر
        Dim iAchat As Integer = posAchat(unAchat.article.id)
        If iAchat <> -1 Then
             ' یافت شد
                Dim achatCourant As Achat = CType(_achats(iAchat), Achat)
            achatCourant.qte += unAchat.qte
        Else
             ' یافت نشد
            _achats.Add(unAchat)
        End If
         ' افزایش مجموع سبد خرید
        _totalPanier += unAchat.totalAchat
    End Sub

     ' حذف یک آیتم
    Public Sub enlever(ByVal idAchat As Integer)
         ' جستجوی آیتم
            Dim iachat As Integer = posAchat(idAchat)
         ' اگر یافت شد، حذف کن
        If iachat <> -1 Then
                Dim achatCourant As Achat = CType(_achats(iachat), Achat)
             ' حذف از سبد خرید
            _achats.RemoveAt(iachat)
             ' کاهش مجموع سبد خرید
            _totalPanier -= achatCourant.totalAchat
        End If
    End Sub

    Private Function posAchat(ByVal idArticle As Integer) As Integer
         ' جستجوی خرید در فهرست خریدها
         'موقعیت آن را در فهرست بازمی‌گرداند یا در صورت عدم یافتن، مقدار -1 را برمی‌گرداند
        Dim achatCourant As Achat
        Dim trouvé As Boolean = False
        Dim i As Integer = 0
            While Not trouvé AndAlso i < _achats.Count
                 ' خرید فعلی
                achatCourant = CType(_achats(i), Achat)
                 ' مقایسه با مورد در حال جستجو
                If achatCourant.article.id = idArticle Then
                    Return i
                End If
                 'خرید بعدی
                i += 1
            End While
             ' یافت نشد
            Return -1
    End Function

     ' تابع هویت
    Public Overrides Function ToString() As String
        Return _achats.ToString
    End Function
End Class

End Namespace

نظرات:

  • کلاس [Panier] دارای خواص و متدهای زیر است:
ReadOnly Property achats() As ArrayList
فهرست خرید مشتری – فهرستی از اشیاء از نوع [Achat]
ajouter(ByVal unAchat As Achat)
اضافه کردن یک خرید به فهرست خرید
enlever(ByVal idAchat As Integer)
حذف خرید آیتم idAchat
ReadOnly Property totalPanier() As Double
مجموع مبلغ اقلام در سبد خرید
Function ToString() As String
ردیف شناسه سبد خرید را برمی‌گرداند
  • متد [posAchat] یک متد کمکی است که جایگاه یک خرید مشخص‌شده با شماره کالا را در لیست خریدها بازمی‌گرداند. فهرست خریدها به گونه‌ای مدیریت می‌شود که یک کالا که چندین بار خریداری شده باشد، تنها یک جایگاه در فهرست را اشغال می‌کند. بنابراین، یک خرید را می‌توان با شماره کالا شناسایی کرد. متد [posAchat] در صورتی که خرید مورد جستجو وجود نداشته باشد، مقدار -1 را برمی‌گرداند.
  • روش [ajouter] یک خرید جدید را به فهرست خریدها اضافه می‌کند. این کار شامل افزودن یک رکورد جدید به فهرست خریدها در صورتی که کالای خریداری‌شده قبلاً در فهرست وجود نداشته باشد، یا افزایش مقدار خریداری‌شده در صورتی که قبلاً وجود داشته باشد، است.
  • متد [enlever] امکان حذف یک خرید با شماره مشخص از فهرست خریدها را فراهم می‌کند. اگر خرید وجود نداشته باشد، این متد هیچ کاری انجام نمی‌دهد.
  • مجموع مبلغ خریدها ([totalPanier]) همزمان با اضافه و حذف خریدها نگهداری می‌شود.

1.4.4.4. کلاس [AchatsArticles]

رابط [IArticlesDomain] توسط کلاس زیر [AchatsArticles] پیاده‌سازی خواهد شد:

Imports istia.st.articles.dao

Namespace istia.st.articles.domain
    Public Class AchatsArticles
        Implements IArticlesDomain

         'فیلدهای خصوصی
        Private _articlesDao As IArticlesDao
        Private _erreurs As ArrayList

         ' سازنده
        Public Sub New(ByVal articlesDao As IArticlesDao)
            _articlesDao = articlesDao
        End Sub

         ' فهرست خطاها
        Public ReadOnly Property erreurs() As ArrayList Implements IArticlesDomain.erreurs
            Get
                Return _erreurs
            End Get
        End Property

         ' فهرست اقلام
        Public Function getAllArticles() As IList Implements IArticlesDomain.getAllArticles
             ' فهرست تمام اقلام
            Try
                Return _articlesDao.getAllArticles
            Catch ex As Exception
                _erreurs = New ArrayList
                _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
            End Try
        End Function

         'بازیابی یک آیتم با شمارهٔ آن
        Public Function getArticleById(ByVal idArticle As Integer) As Article Implements IArticlesDomain.getArticleById
             'یک آیتم مشخص
            Try
                Return _articlesDao.getArticleById(idArticle)
            Catch ex As Exception
                _erreurs = New ArrayList
                _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
            End Try
        End Function


         'خرید یک سبد
        Public Sub acheter(ByVal panier As Panier) Implements IArticlesDomain.acheter
             'خرید یک سبد – سطح موجودی اقلام خریداری‌شده باید کاهش یابد
            _erreurs = New ArrayList
            Dim achat As achat
            Dim achats As ArrayList = panier.achats
            For i As Integer = achats.Count - 1 To 0 Step -1
                 'کاهش موجودی کالا i
                achat = CType(achats(i), achat)
                Try
                    If _articlesDao.changerStockArticle(achat.article.id, -achat.qte) = 0 Then
                         ' عملیات با موفقیت انجام نشد
                        _erreurs.Add("L'achat " + achat.ToString + " n'a pu se faire - Vérifiez les stocks")
                    Else
                         ' عملیات با موفقیت انجام شد – خرید از سبد خرید حذف شد
                        panier.enlever(achat.article.id)
                    End If
                Catch ex As Exception
                    _erreurs = New ArrayList
                    _erreurs.Add("Erreur d'accès aux données : " + ex.Message)
                End Try
            Next
        End Sub
    End Class

End Namespace

توضیحات:

  • این کلاس چهار متد رابط [IArticlesDomain] را پیاده‌سازی می‌کند. این کلاس دو فیلد خصوصی دارد:
_articlesDao As IArticlesDao
شیء دسترسی به داده
_erreurs As ArrayList
فهرست هرگونه خطا. این از طریق ویژگی عمومی [erreurs] قابل دسترسی است.
  • برای ایجاد یک نمونه از کلاس، باید شیئی را که دسترسی به داده‌ها را ممکن می‌سازد، فراهم کنید:
Sub New(ByVal articlesDao As IArticlesDao)
  • متدهای [getAllArticles] و [getArticleById] بر اساس متدهای هم‌نام در لایه [dao] هستند
  • متد [acheter] خرید یک سبد خرید را اعتبارسنجی می‌کند. این اعتبارسنجی صرفاً شامل کاهش موجودی اقلام خریداری‌شده است. یک کالا فقط در صورتی قابل خرید است که موجودی کافی داشته باشد. در غیر این صورت، خرید رد می‌شود: کالا در سبد باقی می‌ماند و یک خطا در لیست [erreurs] گزارش می‌شود. یک خرید تأیید شده از سبد حذف شده و موجودی کالای مربوطه به میزان خرید کاهش می‌یابد.

1.4.4.5. تولید اسمبلی لایه [domain]

پروژه Visual Studio برای تولید اسمبلی [webarticles-domain.dll] پیکربندی شده است. این اسمبلی در پوشه [bin] پروژه تولید می‌شود:

1.4.4.6. NUnit تست‌هایی برای لایه [domain]

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

Image

توضیحات:

  • پروژه [tests] از نوع [bibliothèque de classes] است
  • تست‌های [NUnit] به ارجاعی به اسمبلی [nunit.framework.dll] نیاز دارند
  • کلاس تست [NUnit] یک نمونه از شیء تحت تست را از طریق Spring بازیابی می‌کند. بنابراین،
  • در پوشه [bin]، فایل‌های کلاس Spring
  • در [References]، مرجعی به اسمبلی [Spring-Core.dll] از پوشه [bin]
  • در [bin]، یک فایل پیکربندی برای Spring
  • کلاس تست به اسمبلی [webarticles-dao.dll] از لایه [dao] و اسمبلی [webarticles-domain.dll] از لایه [domain] نیاز دارد. این فایل‌ها در پوشه [bin] قرار داده شده و ارجاع آن‌ها به پروژه اضافه شده است.

یک کلاس آزمایشی NUnit از لایه [domain] ممکن است به شکل زیر باشد:

Imports NUnit.Framework
Imports istia.st.articles.dao
Imports istia.st.articles.domain
Imports Spring.Objects.Factory.Xml
Imports System.IO

Namespace istia.st.articles.tests

    <TestFixture()> _
    Public Class NunitTestArticlesDomain

         ' ابجکت مورد آزمایش
        Private articlesDomain As IArticlesDomain
        Private articlesDao As IArticlesDao

        <SetUp()> _
        Public Sub init()
             ' بازیابی یک نمونه از کارخانه اشیاء Spring
            Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config.xml", FileMode.Open))
             'درخواست نمونه‌سازی شیء DAO مقالات
            articlesDao = CType(factory.GetObject("articlesdao"), IArticlesDao)
             'سپس شیء 'articlesdomain'
            articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDomain)
        End Sub

        <Test()> _
        Public Sub getAllArticles()
             ' بررسی بصری
            listArticles()
        End Sub

        <Test()> _
        Public Sub getArticleById()
             ' حذف مقالات
            articlesDao.clearAllArticles()
             'بررسی
            Dim articles As IList = articlesDomain.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' افزودن ۲ آیتم
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             ' بررسی
            articles = articlesDomain.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' جستجوی آیتم ۳
            Dim unArticle As Article = articlesDomain.getArticleById(3)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article3")
             ' جستجو برای آیتم ۴
            unArticle = articlesDao.getArticleById(4)
             ' تأیید
            Assert.AreEqual(unArticle.nom, "article4")
        End Sub

        <Test()> _
        Public Sub acheterPanier()
             ' حذف آیتم
            articlesDao.clearAllArticles()
             ' تأیید
            Dim articles As IList = articlesDomain.getAllArticles
            Assert.AreEqual(0, articles.Count)
             ' افزودن ۲ آیتم
            articlesDao.ajouteArticle(New Article(3, "article3", 30, 30, 3))
            articlesDao.ajouteArticle(New Article(4, "article4", 40, 40, 4))
             'بررسی
            articles = articlesDomain.getAllArticles
            Assert.AreEqual(2, articles.Count)
             ' ایجاد یک سبد خرید با دو آیتم
            Dim panier As New panier
            panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 10))
            panier.ajouter(New Achat(New Article(4, "article4", 40, 40, 4), 10))
             ' بررسی‌ها
            Assert.AreEqual(700, panier.totalPanier, 0.000001)
            Assert.AreEqual(2, panier.achats.Count)
             'تأیید سبد خرید
            articlesDomain.acheter(panier)
             ' بررسی‌ها
            Assert.AreEqual(0, articlesDomain.erreurs.Count)
            Assert.AreEqual(0, panier.achats.Count)
             'جستجوی آیتم ۳
            Dim unArticle As Article = articlesDomain.getArticleById(3)
             'اعتبارسنجی
            Assert.AreEqual(unArticle.stockactuel, 20)
             'جستجوی آیتم ۴
            unArticle = articlesDao.getArticleById(4)
             'تأیید
            Assert.AreEqual(unArticle.stockactuel, 30)
             'سبد جدید
            panier.ajouter(New Achat(New Article(3, "article3", 30, 30, 3), 100))
             'تأیید سبد خرید
            articlesDomain.acheter(panier)
             ' چک‌ها
            Assert.AreEqual(1, articlesDomain.erreurs.Count)
             ' جستجوی آیتم ۳
            unArticle = articlesDomain.getArticleById(3)
             ' تأیید
            Assert.AreEqual(unArticle.stockactuel, 20)
        End Sub

        <Test()> _
        Public Sub testRetirerAchats()
             'پاک کردن محتویات ARTICLES
            articlesDao.clearAllArticles()
             'جدول ARTICLES را می‌خواند
            Dim articles As IList = articlesDao.getAllArticles()
            Assert.AreEqual(0, articles.Count)
             ' وارد کردن
            Dim article3 As New Article(3, "article3", 30, 30, 3)
            articlesDao.ajouteArticle(article3)
            Dim article4 As New Article(4, "article4", 40, 40, 4)
            articlesDao.ajouteArticle(article4)
             'جدول ARTICLES را می‌خواند
            articles = articlesDomain.getAllArticles()
            Assert.AreEqual(2, articles.Count)
             'یک سبد خرید با دو آیتم ایجاد می‌کند
            Dim monPanier As New Panier
            monPanier.ajouter(New Achat(article3, 10))
            monPanier.ajouter(New Achat(article4, 10))
             'بررسی می‌کند
            Assert.AreEqual(700.0, monPanier.totalPanier, 0.000001)
            Assert.AreEqual(2, monPanier.achats.Count)
             ' افزودن یک آیتم که قبلاً خریداری شده است
            monPanier.ajouter(New Achat(article3, 10))
             'بررسی می‌کند
             ' مجموع باید روی ۱۰۰۰ تنظیم شود
            Assert.AreEqual(1000.0, monPanier.totalPanier, 0.000001)
             ' هنوز ۲ قلم در سبد خرید وجود دارد
            Assert.AreEqual(2, monPanier.achats.Count)
             ' مقدار آیتم ۳ باید به ۲۰ تغییر می‌یافت
            Dim unAchat As Achat = CType(monPanier.achats(0), Achat)
            Assert.AreEqual(20, unAchat.qte)
             ' مورد ۳ از سبد حذف شده است
            monPanier.enlever(3)
             ' بررسی‌ها
             ' مجموع باید به ۴۰۰ به‌روزرسانی شده باشد
            Assert.AreEqual(400.0, monPanier.totalPanier, 0.000001)
             ' فقط یک قلم در سبد
            Assert.AreEqual(1, monPanier.achats.Count)
             ' این باید آیتم شماره ۴ باشد
            Assert.AreEqual(4, CType(monPanier.achats(0), Achat).article.id)
        End Sub

         'فهرست صفحه
        Private Sub listArticles()
            Dim articles As IList = articlesDomain.getAllArticles
            For i As Integer = 0 To articles.Count - 1
                Console.WriteLine(CType(articles(i), Article).ToString)
            Next
        End Sub

    End Class


End Namespace

توضیحات:

  • ما می‌خواستیم یک برنامهٔ تست برای رابط [IArticlesDomain] بنویسیم که مستقل از کلاس پیاده‌سازی آن باشد. بنابراین از Spring استفاده کردیم تا نام کلاس پیاده‌سازی را از برنامهٔ تست پنهان کنیم.
  • متد <Setup()> یک مرجع از Spring برای اشیاء [articlesdomain] و [articlesdao] که قرار است تست شوند، دریافت می‌کند. این‌ها در فایل زیر [spring-config.xml] تعریف شده‌اند:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
    <object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao" />
    <object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain">
        <constructor-arg index="0">
            <ref object="articlesdao" />
        </constructor-arg>
    </object>
</objects>

این فایل بیان می‌کند:

  • (ادامه)
    • برای تک‌نسخه [articlesdao]، نام کلاس پیاده‌سازی [istia.st.articles.dao.ArticlesDaoArrayList] و محل یافتن آن: [ webarticles-dao.dll]. از آنجا که نمونه‌سازی به هیچ پارامتری نیاز ندارد، هیچ‌کدام در اینجا تعریف نشده‌اند.
    • برای تک‌نسخه [articlesdomain]، نام کلاس پیاده‌سازی [istia.st.articles.domain.AchatsArticles] و محل یافتن آن: [ webarticles-domain.dll]. کلاس [AchatsArticles] دارای یک سازنده با یک پارامتر است: تک‌نفره (singleton) که دسترسی به لایه [dao] را مدیریت می‌کند. در اینجا، این به عنوان تک‌نفره (singleton) [articlesdao] که قبلاً تعریف شده است، مشخص شده است.
  • کلاس تست یک نمونه از کلاس تحت تست، [articlesdomain]، و همچنین یک نمونه از کلاس دسترسی به داده‌ها، [articlesdao]، را به دست می‌آورد. این نکته آخر بحث‌برانگیز است. از نظر تئوری، کلاس تست نباید به لایه [dao] دسترسی داشته باشد، لایه‌ای که حتی قرار نیست از آن آگاه باشد. در اینجا، ما این «قاعده» را نادیده گرفته‌ایم که اگر به آن پایبند می‌بودیم، مجبور می‌شدیم متدهای جدیدی در رابط [IArticlesDomain] خود ایجاد کنیم.

برای تست لایه [domain]، ما DLL و [tests-webarticles-domain.dll] را در پوشه [bin] پروژه [tests] تولید می‌کنیم:

سپس، با استفاده از برنامه [Nunit-Gui]، این DLL را بارگذاری کرده و تست‌ها را اجرا می‌کنیم:

Image

خوانندگانی که این سند را روی صفحه نمایش مشاهده می‌کنند، خواهند دید که تمام تست‌ها با موفقیت پشت سر گذاشته شده‌اند. بنابراین، ما فرض می‌کنیم که اکنون یک لایه عملیاتی [domain] داریم.

1.4.5. نتیجه‌گیری

به عنوان یادآوری، ما می‌خواهیم اپلیکیشن وب سه‌لایه زیر را بسازیم:

مدل M برنامه ما MVC اکنون نوشته و آزمایش شده است. این مدل در دو فایل در اختیار ما قرار گرفته است: DLL و [webarticles-dao.dll, webarticles-domain.dll]. اکنون می‌توانیم به لایه نهایی، لایه [web]، که شامل کنترل‌کننده C و ویوهای V است، بپردازیم. ابتدا روشی را که در سند [Développement WEB avec ASP.NET 1.1 ] ارائه شده است، بررسی خواهیم کرد.

  • کنترل‌کننده C توسط دو فایل پیاده‌سازی شده است، [global.asax, main.aspx]
  • ویو‌های V توسط صفحات aspx پیاده‌سازی شده‌اند

1.5. لایه [web]

معماری MVC وب‌اپلیکیشن به شرح زیر خواهد بود:

M=modèle
کلاس‌های کسب‌وکار [domain]، کلاس‌های دسترسی به داده [dao] و منبع داده
V=vues
صفحات ASPX
C=contrôleur
تمام درخواست‌های کلاینت HTTP از طریق دو کنترلر زیر عبور می‌کنند:
global.asax: رویدادهای مربوط به راه‌اندازی اولیهٔ برنامه را مدیریت می‌کند
main.aspx: درخواست هر کلاینت را به‌صورت جداگانه پردازش می‌کند

1.5.1. دیدگاه‌ها

دیدها همان‌هایی هستند که در ابتدای این سند ارائه شده‌اند:

LISTE
liste.aspx
نمای‌ها در پوشه [vues] درون برنامه ذخیره می‌شوند
INFOS
infos.aspx
PANIER
panier.aspx
PANIERVIDE
paniervide.aspx
ERREURS
erreurs.aspx

1.5.2. کنترل‌کننده‌ها

همان‌طور که گفته شد، کنترلر از دو مؤلفه تشکیل خواهد شد:

  1. [global.asax,global.asax.vb]: عمدتاً برای راه‌اندازی برنامه و پر کردن زمینهٔ آن با تمام داده‌هایی که باید بین کلاینت‌های مختلف به اشتراک گذاشته شود، استفاده می‌شود
  2. [main.aspx, main.aspx.vb]: کنترل‌کننده واقعی که درخواست‌های مشتری HTTP را پردازش می‌کند.

درخواست‌های مختلف کلاینت به کنترلر [main.aspx] ارسال می‌شوند و شامل پارامتری به نام [action] هستند که اقدام مورد درخواست کلاینت را مشخص می‌کند:

درخواست
به معنای
عمل کنترل‌کننده
پاسخ‌های ممکن
action=liste
مشتری فهرستی از
اقلام
- درخواست فهرست اقلام از ...
کسب‌وکار
- [LISTE]
- [ERREURS]
action=infos
مشتری درخواست می‌کند
اطلاعاتی درباره یکی از
اقلام نمایش‌داده‌شده در نما
[LISTE]
- آیتم را از لایه کسب‌وکار درخواست می‌کند
- [INFOS]
- [ERREURS]
action=achat
مشتری یک کالا را خریداری می‌کند
- درخواست کالا از لایه کسب‌وکار
و آن را به سبد خرید مشتری اضافه می‌کند
- [INFOS] در صورت وجود خطای مقدار
- [LISTE] در صورت عدم وجود خطا
action=retirerachat
مشتری می‌خواهد یک را حذف کند
خرید را از سبد خرید خود حذف کند
- سبد خرید را از جلسه بازیابی می‌کند
و آن را اصلاح می‌کند
- [PANIER]
- [PANIERVIDE]
- [ERREURS]
action=panier
مشتری می‌خواهد خود را مشاهده کند
سبد خرید
- سبد خرید را از جلسه بازیابی می‌کند
- [PANIER]
- [PANIERVIDE]
- [ERREURS]
action=validationpanier
مشتری خرید خود را تکمیل کرده است
و به مرحله پرداخت می‌رود
- سطوح موجودی را در پایگاه داده به‌روز می‌کند
از اقلام خریداری‌شده
- سبد خرید مشتری را خالی می‌کند
که خریدش تأیید شده است
- [LISTE]
- [ERREURS]

1.5.3. پیکربندی برنامه

ما تلاش خواهیم کرد تا برنامه را طوری پیکربندی کنیم که در برابر تغییراتی مانند موارد زیر تا حد امکان انعطاف‌پذیر باشد:

  1. تغییرات در کلاس‌های URL برای نماهای مختلف
  2. تغییرات در کلاس‌های پیاده‌ساز رابط‌های [IArticlesDao] و [IArticlesDomain]
  3. تغییرات در SGBD، پایگاه داده و جدول محصول

1.5.3.1. تغییرات در URL

نام‌های views URL در فایل پیکربندی برنامه [web.config] به همراه چند پارامتر دیگر قرار خواهند گرفت:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
..
    <appSettings>
        <add key="urlMain" value="/webarticles/main.aspx"/>
        <add key="urlInfos" value="vues/infos.aspx"/>
        <add key="urlErreurs" value="vues/erreurs.aspx"/>
        <add key="urlListe" value="vues/liste.aspx"/>
        <add key="urlPanier" value="vues/panier.aspx"/>
        <add key="urlPanierVide" value="vues/paniervide.aspx"/>
    </appSettings>
</configuration>

1.5.3.2. تغییر کلاس‌های پیاده‌سازی برای رابط‌ها

مطابق با اصول معماری سه‌سطحی، لایه‌ها باید از یکدیگر پنهان‌سازی شوند. این پنهان‌سازی به شرح زیر انجام می‌شود:

  • لایه‌ها به‌جای کلاس‌های مشخص، از طریق رابط‌ها با یکدیگر ارتباط برقرار می‌کنند
  • کد در یک لایه هرگز خودِ کلاس لایه دیگر را برای استفاده از آن instantiate (نمونه سازی) نمی‌کند. این کد صرفاً یک نمونه از پیاده‌سازی رابط را از یک ابزار خارجی – در این مورد، Spring – برای لایه‌ای که می‌خواهد استفاده کند، درخواست می‌کند. برای انجام این کار، می‌دانیم که نیازی نیست نام کلاس پیاده‌سازی را بشناسد، بلکه فقط نام singleton (تک‌نسخه) Spring را که می‌خواهد از آن مرجع بگیرد، می‌داند.

در برنامه کاربردی ما، Spring در فایل [web.config] برنامه کاربردی وب به شرح زیر پیکربندی خواهد شد:

<?xml version="1.0" encoding="iso-8859-1" ?>
<configuration>
    <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>
    <spring>
        <context type="Spring.Context.Support.XmlApplicationContext, Spring.Core">
            <resource uri="config://spring/objects" />
        </context>
        <objects>
    <object id="articlesDao" type="istia.st.articles.dao.ArticlesDaoArrayList, webarticles-dao" />
    <object id="articlesDomain" type="istia.st.articles.domain.AchatsArticles, webarticles-domain">
        <constructor-arg index="0">
            <ref object="articlesDao" />
        </constructor-arg>
    </object>
        </objects>
    </spring>
        <appSettings>
        <add key="urlMain" value="/webarticles/main.aspx"/>
        <add key="urlInfos" value="vues/infos.aspx"/>
        <add key="urlErreurs" value="vues/erreurs.aspx"/>
        <add key="urlListe" value="vues/liste.aspx"/>
        <add key="urlPanier" value="vues/panier.aspx"/>
        <add key="urlPanierVide" value="vues/paniervide.aspx"/>
    </appSettings>
</configuration>

برای دسترسی به لایه [métier]، یک کلاس در لایه [web] می‌تواند singleton [articlesDomain] را درخواست کند. سپس Spring یک شیء از نوع [istia.st.articles.domain.AchatsArticles] را instantiate می‌کند. برای این نمونه‌سازی، به یک شی از نوع [articlesDao] نیاز است، یعنی یک شی از نوع [istia.st.articles.dao.ArticlesDaoArrayList]. اسپرینگ سپس چنین ابجکت را ایجاد می‌کند. در پایان این عملیات، لایه [web] که singleton [articlesDomain] را درخواست کرده است، کل زنجیره‌ای را که آن را به منبع داده متصل می‌کند، در اختیار دارد:

1.5.3.3. تغییراتی که مربوط به SGBD یا پایگاه داده هستند

این مورد در اینجا نادیده گرفته می‌شود زیرا ما در یک برنامه آزمایشی بدون SGBD کار می‌کنیم. ما در مرحله بعد به پیاده‌سازی یک لایه [dao] مبتنی بر SGBD خواهیم پرداخت.

1.5.4. کتابخانه تگ <asp:>

بیایید نمای [ERREURS] را در نظر بگیریم که فهرستی از خطاها را نمایش می‌دهد:

Image

ویو [ERREURS] مسئول نمایش فهرستی از خطاها است که کنترلر [main.aspx] در زمینه درخواست تحت نام [context.Items("erreurs")] قرار داده است. راه‌های مختلفی برای نوشتن چنین صفحه‌ای وجود دارد. در اینجا، ما تنها به بخش نمایش خطاها می‌پردازیم.

به یاد داشته باشید که یک صفحه ASPX دارای یک بخش ارائه (HTML) و یک بخش کد (NET) است که داده‌های قابل نمایش توسط بخش ارائه را آماده می‌کند. این دو بخش ممکن است در یک فایل واحد، [aspx] (راه حل WebMatrix)، یا در دو فایل مجزا قرار گیرند: [aspx] برای بخش ارائه و [aspx.vb] برای کد. راه حل دوم، همان چیزی است که توسط ویژوال استودیو استفاده می‌شود. برای پیچیده‌تر کردن موضوع، بخش ارائه HTML ممکن است حاوی کد .NET نیز باشد که از دیدگاه کاربر تمایز بین [contrôleur] و [présentation] را مبهم می‌کند. این رویکرد عموماً به‌شدت توصیه نمی‌شود. حذف تمام کدها از بخش [présentation] مستلزم ایجاد کتابخانه‌های تگ بود. این کتابخانه‌ها کد را پشت تگ‌هایی که شبیه تگ‌های HTML هستند، پنهان می‌کنند. ما دو راه‌حل ممکن برای صفحه [ERREURS] ارائه می‌دهیم.

راه حل اول ما از کد .NET در بخش [présentation] صفحه استفاده می‌کند. صفحه ASPX لیست خطاهای موجود در درخواست را در بخش کنترل‌کننده [erreurs.aspx.vb] خود بازیابی می‌کند:

        Protected erreurs As ArrayList

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
             ' بازیابی خطاها
            erreurs = CType(context.Items("erreurs"), ArrayList)
        End Sub

و سپس آن را در بخش [présentation, erreurs.aspx] نمایش می‌دهد:

                <h2>Les erreurs suivantes se sont produites :</h2>
                <ul>
                <%
                    for i as integer=0 to erreurs.count-1
                        response.write("<li>" & erreurs(i).ToString & "</li>")
                    next
                %>
                </ul>

راه حل دوم از تگ <asp:repeater> در کتابخانه تگ‌های <asp:> در ASP.NET استفاده می‌کند. اگر یک صفحه ASPX را به‌صورت گرافیکی بسازید، این تگ به‌عنوان یک کامپوننت سرور در دسترس است که می‌توانید آن را به فرم طراحی بکشید و رها کنید. اگر کد ASPX را به‌صورت دستی بنویسید، می‌توانید به آن به‌عنوان یک کتابخانه تگ مراجعه کنید.

با کتابخانه تگ <asp:>، کد ASPX از نمای قبلی [ERREURS] به شکل زیر درمی‌آید:

        <asp:Repeater id="rptErreurs" runat="server">
            <HeaderTemplate>
                <h3>Les erreurs suivantes se sont produites :
                </h3>
                <ul>
            </HeaderTemplate>
            <ItemTemplate>
                <li>
                    <%# Container.DataItem %>
                </li>
            </ItemTemplate>
            <FooterTemplate>
                </ul>
            </FooterTemplate>
        </asp:Repeater>

برچسب

        <asp:Repeater id="rptErreurs" runat="server">

برای تکرار یک الگو HTML در عناصر مختلف یک منبع داده استفاده می‌شود. عناصر مختلف آن به شرح زیر است:

HeaderTemplate
الگوی HTML که قبل از نمایش عناصر منبع داده نمایش داده می‌شود
ItemTemplate
الگوی HTML که باید برای هر عنصر منبع داده تکرار شود. عبارت [<%# Container.DataItem %>] برای نمایش مقدار عنصر فعلی منبع داده استفاده می‌شود.
FooterTemplate
الگوی HTML که پس از نمایش عناصر منبع داده نمایش داده می‌شود

منبع داده به تگ متصل می‌شود، معمولاً در بخش [contrôleur] صفحه:

        Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
..
             'خطاهای پیوند به rptErreurs
            With rptErreurs
                .DataSource = context.Items("erreurs")
                .DataBind()
            End With
        End Sub

این ارتباط همچنین می‌تواند در حین طراحی صفحه برقرار شود اگر منبع داده از قبل مشخص باشد، مانند یک پایگاه داده موجود.

در ویو‌های ما، از تگ دیگری استفاده خواهیم کرد: <asp:datagrid>، که امکان نمایش یک منبع داده به شکل جدول را فراهم می‌کند.

1.5.5. ساختار راه‌حل Visual Studio برای برنامه [webarticles]

یک برنامه وب مانند یک پازل با قطعات بسیار است. پیاده‌سازی معماری MVC معمولاً تعداد این قطعات را افزایش می‌دهد. ساختار برنامه [webarticles] تحت [Visual Studio] به شرح زیر است:

  

توضیحات:

  • پروژه [web] از نوع [bibliothèque de classes] است و نه از نوع [Application web ASP.NET]، همان‌طور که ممکن است به‌طور منطقی انتظار رود. نوع [Application web ASP.NET] نیازمند وجود وب‌سرور IIS بر روی ماشین توسعه یا یک ماشین راه دور است. سرور IIS به‌طور پیش‌فرض روی سیستم‌های Windows XP Home Edition نصب نشده است. با این حال، بسیاری از سیستم‌های PC با این نسخه عرضه می‌شوند. برای اینکه خوانندگانی که از ویندوز XP استفاده می‌کنند بتوانند اپلیکیشن مورد بررسی را پیاده‌سازی کنند، ما از سرور وب Cassini (به ضمیمه مراجعه کنید) که به‌صورت رایگان از مایکروسافت در دسترس است، استفاده خواهیم کرد و پروژه [Application web ASP.NET] را با یک پروژه [bibliothèque de classes] جایگزین خواهیم کرد. این امر چند عیب دارد که در ضمیمه‌ها توضیح داده شده‌اند.
  • فایل‌های DLL مورد استفاده توسط برنامه به شرح زیر هستند:
webarticles-dao.dll
حاوی کلاس‌های لایه دسترسی به داده‌ها است
webarticles-domain.dll
حاوی کلاس‌های لایه کسب‌وکار است
Spring.Core.dll
حاوی کلاس‌های Spring است که به ما امکان می‌دهد لایه‌های وب، دامنه و DAO را یکپارچه کنیم
log4net.dll
کلاس‌های لاگ‌گیری – مورد استفاده Spring

این فایل‌های DLL در پوشه [bin] قرار داده شده و به وابستگی‌های پروژه اضافه می‌شوند.

1.5.6. ویوهای ASPX

همان‌طور که قبلاً توصیه شد، ما در ویوهای ASPX خود از کتابخانه تگ <asp:> استفاده خواهیم کرد.

1.5.6.1. کامپوننت کاربری [entete.ascx]

برای تضمین یکپارچگی در نماهای مختلف، آن‌ها از یک هدر یکسان استفاده می‌کنند که نام برنامه را در کنار منو نمایش می‌دهد:

منو پویا است و توسط کنترلر تعیین می‌شود. کنترلر در درخواست ارسال‌شده به صفحه ASPX یک ویژگی کلید به نام «actions» را قرار می‌دهد که مقدار آن آرایه‌ای از عناصر از نوع Hashtable() است. هر عنصر این آرایه یک دیکشنری است که برای تولید یک گزینه در منوی سربرگ در نظر گرفته شده است. هر دیکشنری دو کلید دارد:

  • href: URL مرتبط با گزینه منو
  • لینک: متن منو

ما هدر را به یک کنترل کاربری تبدیل خواهیم کرد. یک کنترل کاربری بخشی از یک صفحه (قالب‌بندی و کدهای مرتبط) را در قالبی (component) جای می‌دهد که سپس می‌توان آن را در صفحات دیگر مجدداً استفاده کرد. در اینجا، ما می‌خواهیم کامپوننت [entete] را در نماهای دیگر برنامه مجدداً استفاده کنیم. کد نمایش در [entete.ascx] و کد کنترل مربوطه در [entete.ascx.vb] قرار خواهد داشت. کد نمایش از یک کامپوننت <asp:repeater> برای نمایش جدول گزینه‌های منو استفاده خواهد کرد:

type
nom
rôle
۱
تکرارکننده
rptMenu
منبع داده: آرایه‌ای از فرهنگ‌لغت‌ها
با دو کلید: href، link
نمایش گزینه‌های منو

کد نمایش صفحه به شرح زیر خواهد بود:

<%@ Control codebehind="entete.ascx.vb" Language="vb" autoeventwireup="false" inherits="istia.st.articles.web.EnteteWebArticles" %>
        <table>
            <tr>
                <td>
                    <h2>Magasin virtuel</h2></td>
                <asp:Repeater id="rptMenu" runat="server">
                    <ItemTemplate>
                        <td>
                            |<a href='<%# Container.DataItem("href") %>'>
                                <%# Container.DataItem("link") %>
                            </a>
                        </td>
                    </ItemTemplate>
                </asp:Repeater>
            </tr>
        </table>
        <hr>

نظرات:

  • کامپوننت [repeater] در خطوط ۶ تا ۱۴ تعریف شده است
  • هر عنصر در منبع داده مرتبط با تکرارگر یک دیکشنری با دو کلید است: href – خط ۹ و link – خط ۱۰

کد کنترل مربوطه به شرح زیر خواهد بود:

Namespace istia.st.articles.web
    Public Class EnteteWebArticles
        Inherits System.Web.UI.UserControl

        Protected WithEvents rptMenu As System.Web.UI.WebControls.Repeater

        Public WriteOnly Property actions() As Hashtable()
            Set(ByVal Value As Hashtable())
                ' جدول اقدام با مؤلفهٔ خود مرتبط است
                With rptMenu
                    .DataSource = Value
                    .DataBind()
                End With
            End Set
        End Property
    End Class
End Namespace

توضیحات:

  • کامپوننت از نوع [EnteteWebArticles] دارای یک خاصیت عمومی فقط-نوشتن به نام [actions] است – خط ۷
  • این خاصیت اجازه می‌دهد آرایه گزینه‌هایی که توسط کنترل‌کننده برنامه – خطوط ۱۱–۱۲ – محاسبه شده است، با کامپوننت <asp:repeater> با نام [rptMenu] – خط ۱۰ – مرتبط شود.

سایر نماها در برنامه از هدر تعریف‌شده توسط [entete.ascx] استفاده خواهند کرد. برای مثال، صفحه [erreurs.aspx] هدر را با استفاده از کد زیر درج خواهد کرد:

<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<%@ Page inherits="istia.st.articles.web.ErreursWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h3>Les erreurs suivantes se sont produites :
        </h3>    

توضیحات:

  • خط ۱ مشخص می‌کند که تگ <WA:entete> باید با کامپوننتی که توسط فایل [entete.ascx] تعریف شده است، مرتبط باشد. ویژگی‌های [TagPrefix] و [TagName] را می‌توان در صورت نیاز تنظیم کرد.
  • پس از انجام این کار، این کامپوننت از طریق خط ۹ در کد ارائه صفحه درج می‌شود. در زمان اجرا، این تگ باعث می‌شود کد صفحه ASPX که حاوی آن است، کد صفحه [entete.ascx] را شامل شود. کد کنترلی [erreurs.aspx.vb] این کامپوننت را راه‌اندازی می‌کند. این کار را به شرح زیر انجام می‌دهد:
    Public Class ErreursWebarticles
        Inherits System.Web.UI.Page

         ' اجزای صفحه
...
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
             ' گزینه‌های منو به rptmenu متصل هستند
            entete.actions = CType(context.Items("options"), Hashtable())
...
        End Sub
    End Class

توضیحات:

  • خط ۶ یک شیء از نوع [EnteteWebArticles] ایجاد می‌کند که نوع کامپوننت ایجادشده است
  • خط ۱۱ خاصیت [actions] این شیء را مقداردهی اولیه می‌کند

1.5.6.2. نما [liste.aspx]

1.5.6.2.1. Introduction

این نما لیست اقلام موجود برای فروش را نمایش می‌دهد:

این نما پس از درخواست /main?action=list یا /main?action=cartvalidation نمایش داده می‌شود. پارامترهای درخواست کنترلر به شرح زیر است:

actions
شیء Hashtable() – آرایهٔ گزینه‌های منو
listarticles
ابجکت‌های نوع [Article]
message
شیء رشته‌ای – پیامی که در پایین صفحه نمایش داده می‌شود

هر لینک [Infos] در جدول HTML مقالات دارای یک URL از شکل [?action=infos&id=ID] است، که در آن ID فیلد id از ...مقاله‌ای که در حال نمایش است.

1.5.6.2.2. اجزای صفحه
type
nom
rôle
1
کامپوننت کاربر
سربرگ
سربرگ نمایش
2
DataGrid
DataGridArticles
3 – ستون مرتبط: سربرگ: نام، فیلد: name
4 – ستون مرتبط: سربرگ: قیمت، فیلد: قیمت
۵ - ستون هایپرمتن: متن: Info، فیلد URL: id,
قالب URL: /webarticles/main.aspx?action=infos&id={0}
نمایش اقلام برای فروش
6
برچسب
lblMessage
نمایش یک پیام

بیایید مرور کنیم که چگونه این ویژگی‌ها را تنظیم کنیم:

  • در ویژوال استودیو، [DataGrid] را برای دسترسی به برگهٔ ویژگی‌های آن انتخاب کنید:

Image

  • برای مدیریت چیدمان جدول نمایش‌داده‌شده، از لینک [Mise en forme automatique] بالا استفاده کنید
  • و لینک [Générateur de propriétés] برای مدیریت محتوای آن
1.5.6.2.3. کد ارائه [liste.aspx]
<%@ Page codebehind="liste.aspx.vb" inherits="istia.st.articles.web.ListeWebarticles" autoeventwireup="false" Language="vb" %>
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx"%>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Liste des articles</h2>
        <P>
            <asp:DataGrid id="DataGridArticles" runat="server" ForeColor="Black" BackColor="LightGoldenrodYellow"
                BorderColor="Tan" CellPadding="2" BorderWidth="1px" GridLines="None" AutoGenerateColumns="False">
                <SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
                <HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
                <FooterStyle BackColor="Tan"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Nom"></asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix" DataFormatString="{0:C}"></asp:BoundColumn>
                    <asp:HyperLinkColumn Text="Infos" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=infos&amp;id={0}"></asp:HyperLinkColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Center" ForeColor="DarkSlateBlue" BackColor="PaleGoldenrod"></PagerStyle>
            </asp:DataGrid></P>
        <P>
            <asp:Label id="lblMessage" runat="server" BackColor="#FFC080"></asp:Label></P>
    </body>
</HTML>

نظرات:

  • خط ۹ هدر صفحه را تعریف می‌کند
  • خطوط ۱۲ تا ۲۴ ویژگی‌های [DataGrid] را تعریف می‌کنند
  • خط ۲۶ برچسب [lblMessage] را تعریف می‌کند
1.5.6.2.4. کد کنترل‌کننده [liste.aspx.vb]
Imports System
Imports System.Collections
Imports System.Data
Imports istia.st.articles.dao

Namespace istia.st.articles.web

     'مدیریت صفحه نمایش‌دهندهٔ فهرست آیتم‌ها را بر عهده دارد
    Public Class ListeWebarticles
        Inherits System.Web.UI.Page

         ' اجزای صفحه
        Protected WithEvents lblMessage As System.Web.UI.WebControls.Label
        Protected WithEvents DataGridArticles As System.Web.UI.WebControls.DataGrid
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             'نمای [liste] را بر اساس اطلاعات زمینه آماده می‌کند
             ' گزینه‌های منو را به rptmenu پیوند می‌دهد
            entete.actions = CType(context.Items("options"), Hashtable())
             ' بازیابی مقالات در یک DataTable
            Dim articles As ArrayList = CType(context.Items("articles"), ArrayList)
             ' آنها را به کامپوننت [DataGrid] در صفحه متصل می‌کند
            With DataGridArticles
                .DataSource = articles
                .DataBind()
            End With
             'پیام نمایش داده می‌شود
            lblMessage.Text = context.Items("message").ToString
        End Sub
    End Class
End Namespace

توضیحات:

  • اجزای صفحه در خطوط ۱۳–۱۵ ظاهر می‌شوند. توجه داشته باشید که ما نیاز داشتیم یک شیء [EnteteWebArticles] را با یک اپراتور [new] ایجاد کنیم، در حالی که این کار برای سایر اجزا ضروری نبود. بدون این ایجاد صریح، با خطای زمان اجرا مواجه شدیم که اعلام می‌کرد شیء [entete] به هیچ‌چیز ارجاع نمی‌دهد. این نکته ارزش بررسی بیشتر را داشت. به آن رسیدگی نشد.
  • جدول گزینه‌های منوی بالا (header menu) برای راه‌اندازی کامپوننت [entete] صفحه از زمینه گرفته می‌شود – خط ۲۰
  • فهرست آیتم‌ها از زمینه گرفته می‌شود – خط ۲۲
  • برای راه‌اندازی کامپوننت [DataGridArticles] – خطوط 24–27
  • کامپوننت [lblMessage] با پیامی که در زمینه قرار داده شده است، مقداردهی اولیه می‌شود – خط ۲۹

1.5.6.3. نما [infos.aspx]

1.5.6.3.1. Introduction

این نما اطلاعاتی درباره یک آیتم نمایش می‌دهد و همچنین امکان خرید آن را فراهم می‌کند:

Image

این صفحه پس از درخواست به /main?action=infos&id=ID یا درخواست به /main?action=achat&id=ID زمانی که مقدار خریداری‌شده نادرست باشد، نمایش داده می‌شود. اجزای درخواست کنترلر به شرح زیر است:

actions
شیء Hashtable() – آرایه گزینه‌های منو
article
شیء از نوع [Article] – موردی که باید نمایش داده شود
msg
ابژه رشته‌ای – پیامی برای نمایش در صورت خطای مقدار
qte
ابژه رشته‌ای – مقداری که باید در فیلد ورودی [Qte] نمایش داده شود

fields [msg] و [qte] در صورت بروز خطای ورودی در مورد مقدار استفاده می‌شوند:

Image

این صفحه حاوی فرمى است که از طریق دکمه [Acheter] ارسال مى‌شود. هدف URL برای POST، [?action=achat&id=ID] است، که در آن ID شناسه کالای خریداری‌شده است.

1.5.6.3.2. اجزای صفحه
type
nom
rôle
1
کامپوننت کاربر
سربرگ
سربرگ نمایش
2
متنی
litID
نمایش شماره آیتم
3 à 6
DataGrid
DataGridArticle
3 – ستون مرتبط: سربرگ: Name، فیلد: name
4 – ستون مرتبط: سربرگ: قیمت، فیلد: price
۵ - ستون مرتبط: سربرگ: موجودی فعلی، فیلد: stockActuel
۶ - ستون مرتبط: سربرگ: حداقل موجودی، فیلد: stockMinimum
نمایش یک آیتم
7
HTML ارسال
 
ارسال فرم
8
HTML ورودی
runat=server
txtQte
مقدار خریداری‌شده را وارد کنید
9
برچسب
lblMsgQte
هر پیام خطا
1.5.6.3.3. کد نمایش [infos.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<%@ Page codebehind="infos.aspx.vb" inherits="istia.st.articles.web.InfosWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Article d'id [<asp:Literal id="litId" runat="server"></asp:Literal>]</h2>
        <P>
            <asp:DataGrid id="DataGridArticle" runat="server" BackColor="White" BorderColor="#E7E7FF" CellPadding="3"
                BorderWidth="1px" BorderStyle="None" GridLines="Horizontal" AutoGenerateColumns="False">
                <SelectedItemStyle Font-Bold="True" ForeColor="#F7F7F7" BackColor="#738A9C"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="#F7F7F7"></AlternatingItemStyle>
                <ItemStyle HorizontalAlign="Center" ForeColor="#4A3C8C" BackColor="#E7E7FF"></ItemStyle>
                <HeaderStyle Font-Bold="True" HorizontalAlign="Center" ForeColor="#F7F7F7" BackColor="#4A3C8C"></HeaderStyle>
                <FooterStyle ForeColor="#4A3C8C" BackColor="#B5C7DE"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Nom">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix" DataFormatString="{0:C}">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="stockactuel" HeaderText="Stock actuel">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                    <asp:BoundColumn DataField="stockminimum" HeaderText="Stock minimum">
                        <HeaderStyle HorizontalAlign="Center"></HeaderStyle>
                    </asp:BoundColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Right" ForeColor="#4A3C8C" BackColor="#E7E7FF" Mode="NumericPages"></PagerStyle>
            </asp:DataGrid></P>
        <HR width="100%" SIZE="1">
        <form method="post" action="<%=strAction%>">
            <table>
                <tr>
                    <td><input type="submit" value="Acheter"></td>
                    <td>Qté</td>
                    <td><INPUT type="text" maxLength="3" size="3" id="txtQte" runat="server"></td>
                    <td><asp:Label id="lblMsgQte" runat="server" />
                    </td>
                </tr>
            </table>
        </form>
    </body>
</HTML>

توضیحات:

  • سربرگ در صفحه گنجانده شده است – خط ۹
  • متن [litId] در خط ۱۰ تعریف شده است
  • DataGrid و [DataGridArticles] در خطوط ۱۲–۳۴ تعریف شده‌اند
  • فرم در خطوط ۳۶–۴۶ تعریف شده است. نوع آن POST است.
  • هدف برای POST توسط متغیر [strAction] – خط ۳۶ – فراهم می‌شود. این متغیر باید توسط کنترلر تعریف شود.
  • میدان ورودی برای مقدار خریداری‌شده در خط ۴۱ تعریف شده است. این یک کامپوننت سمت سرور (runat=server) از نوع HTML است. در کد، به آن از طریق یک شی دسترسی پیدا می‌شود.
  • خط ۴۲ برچسب [lblMsgQte] را تعریف می‌کند که حاوی هرگونه پیام خطا مربوط به مقدار وارد شده خواهد بود
1.5.6.3.4. کد کنترل [infos.aspx.vb]
Imports istia.st.articles.dao
Imports System
Imports System.Collections

Namespace istia.st.articles.web

     'صفحه اطلاعات محصول را مدیریت می‌کند
    Public Class InfosWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents lblMsgQte As System.Web.UI.WebControls.Label
        Protected WithEvents litId As System.Web.UI.WebControls.Literal
        Protected WithEvents txtQte As System.Web.UI.HtmlControls.HtmlInputText
        Protected WithEvents DataGridArticle As System.Web.UI.WebControls.DataGrid
        Protected WithEvents entete As New EnteteWebArticles

         ' URL که فرم به آن ارسال خواهد شد
        Private _strAction As String
        Public Property strAction() As String
            Get
                Return _strAction
            End Get
            Set(ByVal Value As String)
                _strAction = Value
            End Set
        End Property

         'نمایش صفحه
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' اطلاعات را از درخواست بازیابی می‌کند
            Dim unArticle As Article = CType(Session.Item("article"), Article)
             ' گزینه‌های منو را به rptmenu پیوند می‌دهد
            entete.actions = CType(context.Items("options"), Hashtable())
             ' مقاله را به [DataGrid] پیوند می‌دهد
            Dim articles As New ArrayList
            articles.Add(unArticle)
            With DataGridArticle
                .DataSource = articles
                .DataBind()
            End With
             'شناسه برچسب
            litId.Text = unArticle.id.ToString
             ' پیام خطا
            lblMsgQte.Text = context.Items("msg").ToString
             ' مقدار قبلی
            txtQte.Value = context.Items("qte").ToString
             ' اقدام
            strAction = "?action=achat&id=" + unArticle.id.ToString
        End Sub
End Namespace

توضیحات:

  • اجزای صفحه در خطوط ۱۰–۱۴ تعریف شده‌اند
  • کلاس یک ویژگی عمومی [strAction] را تعریف می‌کند که برای تعیین هدف فرم POST استفاده می‌شود – خطوط 17–25
  • مقاله‌ای که باید نمایش داده شود از زمینهٔ برنامه بازیابی می‌شود – خط ۳۰
  • جدول گزینه‌ها برای منوی سربرگ از زمینه گرفته می‌شود تا کامپوننت [entete] صفحه را راه‌اندازی کند – خط ۳۲
  • خطوط ۳۳–۳۹: کامپوننت [DataGridArticle] به یک منبع داده از نوع [ArrayList] متصل می‌شود که تنها شامل آیتمی است که در خط ۳۰ بازیابی شده است
  • کامپوننت‌های [lblMsgQte, txtQte] با اطلاعاتی که از زمینه گرفته شده، مقداردهی اولیه می‌شوند – خطوط 42–45
  • ویژگی [straction] نیز با اطلاعاتی که از زمینه گرفته شده است مقداردهی اولیه می‌شود – خط ۴۷. این متغیر برای تولید صفت [action] از نوع HTML که در صفحه وجود دارد، استفاده می‌شود:
        <form method="post" action="<%=strAction%>">
....
        </form>

1.5.6.4. نما [panier.aspx]

1.5.6.4.1. Introduction

این نما محتویات سبد را نمایش می‌دهد:

Image

این صفحه پس از درخواست به /main?action=basket یا /main?action=cancelpurchase&id=ID نمایش داده می‌شود. پارامترهای درخواست کنترلر به شرح زیر است:

actions
شیء Hashtable() – آرایهٔ گزینه‌های منو
panier
شیء از نوع [Panier] – سبد خریدی که باید نمایش داده شود

هر لینک [Retirer] در آرایه HTML اقلام سبد خرید دارای یک URL به شکل [?action=retirerachat&id=ID] است، که در آن ID نشان‌دهنده QZX است.میدان W2HTMLP000461ZQX مربوط به کالایی که باید از سبد خرید حذف شود.

1.5.6.4.2. اجزای صفحه
type
nom
rôle
1
کامپوننت کاربر
سربرگ
سربرگ نمایش
2
DataGrid
DataGridAchats
3 – ستون مرتبط – سربرگ: Item، فیلد: name
4 – ستون مرتبط – سربرگ: Qty، فیلد: qty
۵ – ستون مرتبط – سربرگ: قیمت، فیلد: price
6 – ستون مرتبط – سربرگ: Total، فیلد: total، قالب‌بندی {0:C}
7 – ستون هایپرمتن – متن: حذف، URL: id، قالب URL: /webarticles/main.aspx?action=retirerachat&id={0}
نمایش لیست اقلام خریداری شده
8
برچسب
lblTotal
نمایش مبلغ قابل پرداخت
1.5.6.4.3. کد نمایش [panier.aspx]
<%@ Page codebehind="panier.aspx.vb" inherits="istia.st.articles.web.PanierWebarticles" autoeventwireup="false" Language="vb" %>
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Contenu de votre panier</h2>
        <P>
            <asp:DataGrid id="DataGridAchats" runat="server" BorderWidth="1px" GridLines="Vertical" CellPadding="4"
                BackColor="White" BorderStyle="None" BorderColor="#DEDFDE" ForeColor="Black" AutoGenerateColumns="False">
                <SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#CE5D5A"></SelectedItemStyle>
                <AlternatingItemStyle BackColor="White"></AlternatingItemStyle>
                <ItemStyle BackColor="#F7F7DE"></ItemStyle>
                <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#6B696B"></HeaderStyle>
                <FooterStyle BackColor="#CCCC99"></FooterStyle>
                <Columns>
                    <asp:BoundColumn DataField="nom" HeaderText="Article"></asp:BoundColumn>
                    <asp:BoundColumn DataField="qte" HeaderText="Qt&#233;"></asp:BoundColumn>
                    <asp:BoundColumn DataField="prix" HeaderText="Prix"></asp:BoundColumn>
                    <asp:BoundColumn DataField="totalAchat" HeaderText="Total" DataFormatString="{0:C}"></asp:BoundColumn>
                    <asp:HyperLinkColumn Text="Retirer" DataNavigateUrlField="id" DataNavigateUrlFormatString="/webarticles/main.aspx?action=retirerachat&amp;id={0}"></asp:HyperLinkColumn>
                </Columns>
                <PagerStyle HorizontalAlign="Right" ForeColor="Black" BackColor="#F7F7DE" Mode="NumericPages"></PagerStyle>
            </asp:DataGrid></P>
        <P>Total de la commande :
            <asp:Label id="lblTotal" runat="server"></asp:Label>&nbsp;euros</P>
    </body>
</HTML>

توضیحات

  • خط ۹ شامل سربرگ است
  • خطوط ۱۲–۲۷: کامپوننت [DataGridAchats] تعریف شده است
  • خط ۲۹: کامپوننت [lblTotal] تعریف شده است
1.5.6.4.4. کد کنترل [panier.aspx.vb]
Imports System
Imports System.Collections
Imports System.Data
Imports istia.st.articles.dao
Imports istia.st.articles.domain

Namespace istia.st.articles.web
     ' صفحه نمایش سبد خرید را مدیریت می‌کند
    Public Class PanierWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents DataGridAchats As System.Web.UI.WebControls.DataGrid
        Protected WithEvents lblTotal As System.Web.UI.WebControls.Label
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             ' گزینه‌های منو را به rptmenu پیوند می‌دهد
            entete.actions = CType(context.Items("options"), Hashtable())
             'سبد خرید را بازیابی می‌کند
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             'خریدها را به جدولی از خطوط خرید منتقل می‌کند
            Dim achats(unPanier.achats.Count - 1) As LigneAchat
             'نوع عناصر را در ArrayList تغییر می‌دهد
            For i As Integer = 0 To achats.Length - 1
                achats(i) = New LigneAchat(CType(unPanier.achats(i), Achat))
            Next
             ' داده‌ها را به اجزای [DataGrid] در صفحه پیوند می‌دهد
            With DataGridAchats
                .DataSource = achats
                .DataBind()
            End With
             'نمایش مبلغ کل قابل پرداخت
            lblTotal.Text = unPanier.totalPanier.ToString
        End Sub

         ' خط خرید ایجاد شده از یک شیء خرید
        Private Class LigneAchat
            Inherits Achat

             ' سازنده یک خرید دریافت می‌کند
            Public Sub New(ByVal unAchat As Achat)
                Me.article = unAchat.article
                Me.qte = unAchat.qte
            End Sub

             ' id: شناسهٔ کالای خریداری‌شده را برمی‌گرداند
            Public ReadOnly Property id() As Integer
                Get
                    Return article.id
                End Get
            End Property

             ' name: نام کالای خریداری‌شده
            Public ReadOnly Property nom() As String
                Get
                    Return article.nom
                End Get
            End Property

             ' قیمت کالای خریداری‌شده
            Public ReadOnly Property prix() As Double
                Get
                    Return article.prix
                End Get
            End Property

        End Class
    End Class
End Namespace

توضیحات:

  • اجزای صفحه در خطوط 11–13 اعلام شده‌اند
  • ابتدایی‌سازی کامپوننت [entete] با آنچه در صفحات قبلاً بررسی‌شده یافت می‌شود یکسان است – خط 17
  • سبد خریدی که باید نمایش داده شود از جلسه بازیابی می‌شود – خط ۱۹
  • نمایش این سبد خرید با استفاده از کامپوننت [DataGridAchats] مشکلاتی را به همراه دارد. مشکل از نحوهٔ инициализация این کامپوننت ناشی می‌شود. بیایید ستون‌های آن را مرور کنیم:
    • ستون [Article] مرتبط با فیلد [nom] در منبع داده
    • ستون [Qté] مرتبط با فیلد [qte] در منبع داده
    • ستون [Prix] مرتبط با فیلد [prix] در منبع داده
    • ستون [Total] مرتبط با فیلد [total] در منبع داده

منبع داده‌ای که در اختیار داریم سبد خرید و فهرست خرید آن است. مورد دوم منبع داده برای [DataGrid] خواهد بود. تنها اشیاء [Achat] که سطرهای [DataGrid] را پر می‌کنند، دارای ویژگی‌های [nom, qte, prix, total] مورد انتظار [DataGrid] نیستند. بنابراین، یک منبع داده به‌طور خاص برای [DataGrid] ایجاد می‌شود که عناصر آن دارای ویژگی‌های مورد انتظار [DataGrid] هستند. این عناصر از نوع [LigneAchat] خواهند بود، کلاسی که برای این منظور ایجاد شده و از کلاس [Achat] مشتق شده است – خطوط ۳۶–۶۶

  • پس از تعریف کلاس [LigneAchat]، منبع داده برای [DataGridAchats] از سبد موجود در جلسه ساخته می‌شود – خطوط ۲۰–۳۰
  • مبلغ کل خرید با استفاده از خاصیت [totalPanier] از کلاس [Panier] نمایش داده می‌شود – خط ۳۲

1.5.6.5. نما [paniervide.aspx]

1.5.6.5.1. Introduction

این نما اطلاعاتی را نمایش می‌دهد که نشان می‌دهد سبد خرید خالی است:

Image

این صفحه پس از درخواست /main?action=basket یا /main?action=checkout&id=ID نمایش داده می‌شود. پارامترهای درخواست کنترلر به شرح زیر است:

actions
شیء Hashtable() – آرایه گزینه‌های منو
1.5.6.5.2. اجزای صفحه
type
nom
rôle
1
کامپوننت کاربر
سربرگ
سربرگ نمایش
1.5.6.5.3. کد ارائه [paniervide.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx"%>
<%@ Page codebehind="paniervide.aspx.vb" inherits="istia.st.articles.web.PaniervideWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h2>Contenu de votre panier</h2>
        <P>Votre panier est vide</P>
    </body>
</HTML>

نظرات:

  • سربرگ در خط ۹ درج شده است
1.5.6.5.4. کد کنترلی [paniervide.aspx.vb]
Namespace istia.st.articles.web
     ' صفحه نمایش سبد خالی را مدیریت می‌کند
    Public Class PaniervideWebarticles
        Inherits System.Web.UI.Page
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             'نمای [paniervide] را بر اساس اطلاعات زمینه آماده می‌کند
             ' گزینه‌های منو را به rptmenu پیوند می‌دهد
            entete.actions = CType(context.Items("options"), Hashtable())
        End Sub
    End Class
End Namespace

توضیحات:

  • ما به سادگی تنها مؤلفهٔ پویا صفحه را مقداردهی اولیه می‌کنیم – خط ۱۰

1.5.6.6. نما [erreurs.aspx]

1.5.6.6.1. Introduction

این صفحه در صورت بروز خطاها نمایش داده می‌شود:

Image

این صفحه پس از هر درخواستی که منجر به خطا شود نمایش داده می‌شود، به جز اقدام خرید با مقدار نادرست که توسط نما [INFOS] پردازش می‌شود. عناصر درخواست کنترل‌کننده به شرح زیر است:

actions
شیء Hashtable() – آرایه گزینه‌های منو
erreurs
ابجکت‌های ArrayList، که هر کدام حاوی ابجکت‌های [String] هستند که نمایانگر پیام‌های خطایی هستند که باید نمایش داده شوند
1.5.6.6.2. اجزای صفحه
type
nom
rôle
1
کامپوننت کاربر
سربرگ
سربرگ نمایش
2
تکرارکننده
rptErreurs
نمایش فهرست خطاها
1.5.6.6.3. کد نمایش [erreurs.aspx]
<%@ Register TagPrefix="WA" TagName="entete" Src="entete.ascx" %>
<%@ Page codebehind="erreurs.aspx.vb" inherits="istia.st.articles.web.ErreursWebarticles" autoeventwireup="false" Language="vb" %>
<HTML>
    <HEAD>
        <TITLE>webarticles</TITLE>
        <META http-equiv="Content-Type" content="text/html; charset=windows-1252">
    </HEAD>
    <body>
        <WA:entete id="entete" runat="server"></WA:entete>
        <h3>Les erreurs suivantes se sont produites :
        </h3>
        <ul>
            <asp:Repeater id="rptErreurs" runat="server">
                <ItemTemplate>
                    <li>
                        <%#Container.DataItem %>
                    </li>
                </ItemTemplate>
            </asp:Repeater></ul>
    </body>
</HTML>

توضیحات:

  • سربرگ در خط ۹ تعریف شده است
  • کامپوننت [rptErreurs] در خطوط ۱۳ تا ۱۹ تعریف شده است. محتوای آن از یک منبع داده از نوع [ArrayList] می‌آید که شامل اشیایی از نوع [String] است.
1.5.6.6.4. کد کنترل [erreurs.aspx.vb]
Namespace istia.st.articles.web

     ' صفحه خطا را مدیریت می‌کند
    Public Class ErreursWebarticles
        Inherits System.Web.UI.Page

         ' اجزای صفحه
        Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
        Protected WithEvents entete As New EnteteWebArticles

        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             'نمای [erreurs] با استفاده از اطلاعات زمینه آماده می‌شود
             'گزینه‌های منو به rptmenu متصل هستند
            entete.actions = CType(context.Items("options"), Hashtable())
             'خطاها به rptErreurs مرتبط هستند
            With rptErreurs
                .DataSource = context.Items("erreurs")
                .DataBind()
            End With
        End Sub
    End Class

End Namespace

توضیحات:

  • کامپوننت [entete] طبق معمول مقداردهی اولیه می‌شود، خطوط ۹ و ۱۴
  • کامپوننت [rptErreurs] با فهرست خطاهای نوع [ArrayList] یافت‌شده در زمینه – خطوط 16–19 – مقداردهی اولیه می‌شود

1.5.7. کنترل‌کننده‌های global.asax و main.aspx

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

  • استخراج درخواست مشتری،
  • پردازش اقدام درخواستی مشتری با استفاده از کلاس‌های کسب‌وکار،
  • بازگرداندن نمای مناسب.

1.5.7.1. کنترل‌کننده [global.asax.vb]

وقتی برنامه اولین درخواست خود را دریافت می‌کند، رویه [Application_Start] در فایل [global.asax.vb] اجرا می‌شود. این تنها یک بار اتفاق می‌افتد. هدف رویه [Application_Start]، راه‌اندازی اشیاء مورد نیاز وب‌اپلیکیشن است که به‌صورت فقط خواندنی توسط تمام نخ‌های کلاینت مشترک خواهند بود. این اشیاء مشترک را می‌توان در دو مکان قرار داد:

  • متغیرهای خصوصی کنترلر
  • زمینهٔ اجرای برنامه (Application)

متد [Application_Start] از برنامه [global.asax.vb] اقدامات زیر را انجام خواهد داد:

  • فایل [web.config] را برای پارامترهای مورد نیاز برای عملکرد صحیح برنامه بررسی می‌کند. این موارد در بخش 1.5.3 توضیح داده شده‌اند.
  • هر فهرست خطا را در قالب یک شیء [ArrayList erreurs] در زمینهٔ برنامه قرار می‌دهد. این فهرست در صورت عدم وجود خطا خالی خواهد بود، اما با این وجود وجود خواهد داشت.
  • اگر خطایی رخ داده باشد، متد [Application_Start] در این نقطه متوقف می‌شود. در غیر این صورت، یک مرجع به یک شیء تک‌نسل (singleton) از نوع [IArticlesDomain] را درخواست می‌کند، که این شیء، شیء کسب‌وکاری است که کنترل‌کننده برای مقاصد خود از آن استفاده خواهد کرد. همانطور که در بخش 1.5.3.2 توضیح داده شد، کنترلر این اشیای تک‌نفره را از فریم‌ورک Spring درخواست خواهد کرد. این فرآیند نمونه‌سازی ممکن است به خطاهای مختلفی منجر شود. در این صورت، این خطاها مجدداً در شی [erreurs] در زمینه برنامه ذخیره خواهند شد.

کنترل‌کننده [global.asax.vb] روندی به نام [Session_Start] دارد که هر بار یک مشتری جدید وارد می‌شود، اجرا می‌گردد. در این روند، یک سبد خرید خالی برای مشتری ایجاد می‌شود. این سبد در تمام درخواست‌های آن مشتری خاص حفظ می‌شود. کد ممکن است به این شکل باشد:

Imports System
Imports System.Web
Imports System.Web.SessionState
Imports System.Configuration
Imports istia.st.articles.domain
Imports System.Collections
Imports Spring.Context

Namespace istia.st.articles.web

    Public Class GlobalWebArticles
        Inherits System.Web.HttpApplication

         ' راه‌اندازی برنامه
        Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

             ' داده‌های محلی
            Dim parameters() As String = {"urlMain", "urlErreurs", "urlInfos", "urlListe", "urlPanier", "urlPanierVide"}
            Dim erreurs As New ArrayList

             ' بازیابی پارامترهای راه‌اندازی برنامه
            Dim param As String
            For i As Integer = 0 To parameters.Length - 1
                 ' خواندن از فایل پیکربندی
                param = ConfigurationSettings.AppSettings(parameters(i))
                If param Is Nothing Then
                     ' خطا را ثبت کنید
                    erreurs.Add("Paramètre [" + parameters(i) + "] absent dans le fichier [web.config]")
                Else
                     'پارامتر در برنامه ذخیره می‌شود
                    Application.Item(parameters(i)) = param
                End If
            Next
             ' آیا خطایی وجود دارد؟
            If erreurs.Count = 0 Then
                 ' برای دسترسی به لایه کسب‌وکار یک شیء IArticlesDomain ایجاد کنید
                Dim contexte As IApplicationContext = CType(ConfigurationSettings.GetConfig("spring/context"), IApplicationContext)
                Dim articlesDomain As IArticlesDomain
                Try
                    articlesDomain = CType(contexte.GetObject("articlesDomain"), IArticlesDomain)
                     ' شیء در برنامه ذخیره می‌شود
                    Application.Item("articlesDomain") = articlesDomain
                Catch ex As Exception
                     'خطا ذخیره می‌شود
                    erreurs.Add("Erreur lors de la construction de l'objet d'accès à la couche métier [" + ex.ToString + "]")
                End Try
            End If
             'خطاها در برنامه قرار داده می‌شوند
            Application.Item("erreurs") = erreurs
             ' اگر خطایی وجود داشته باشد، فرآیند کامل می‌شود
            If erreurs.Count <> 0 Then Return
             'یک آرایه از گزینه‌های منو ایجاد می‌شود
            Dim options As New Hashtable
             ' URL را از کنترلر بازیابی می‌کند
            Dim urlMain As String = CType(Application.Item("urlMain"), String)
            Dim uneOption As Hashtable
             ' فهرست اقلام
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=liste")
            uneOption.Add("lien", "Liste des articles")
            options.Add("liste", uneOption)
             ' سبد خرید
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=panier")
            uneOption.Add("lien", "Voir le panier")
            options.Add("panier", uneOption)
             'تأیید سبد خرید
            uneOption = New Hashtable
            uneOption.Add("href", urlMain + "?action=validationpanier")
            uneOption.Add("lien", "Valider le panier")
            options.Add("validationpanier", uneOption)
             ' تنظیم گزینه‌های منو در برنامه
            Application.Item("options") = options
            Return
        End Sub

         ' ابتدای جلسه
        Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
             ' ایجاد سبد خرید برای مشتری
            Session.Item("panier") = New Panier
        End Sub
    End Class
End Namespace

توضیحات:

  • پارامترهای مورد انتظار در [web.config] در یک آرایه تعریف شده‌اند – خط ۱۸
  • در [web.config] بررسی می‌شوند. اگر موجود باشند، در زمینهٔ برنامه ذخیره می‌شوند؛ در غیر این صورت، یک خطا در لیست خطاهای [erreurs] ثبت می‌شود – خطوط 21–33
  • اگر خطایی وجود نداشته باشد، از Spring درخواست می‌شود تا مرجعی به singleton [articlesDomain] که مدیریت دسترسی به لایه [domain] برنامه را بر عهده دارد، ارائه دهد – خطوط 35–47. هرگونه خطا در [erreurs] ثبت می‌شود.
  • خطاها در زمینهٔ برنامه ثبت می‌شوند – خط ۴۹
  • در صورت وقوع هرگونه خطا، رویه متوقف می‌شود – خط ۵۱
  • یک آرایه از سه دیکشنری ایجاد می‌شود. هر یک دو کلید دارد: href و link. این آرایه نمایانگر سه گزینهٔ ممکن منو است – خطوط 52–71
  • این آرایه در زمینهٔ برنامه ذخیره می‌شود – خط ۷۳
  • برای هر مشتری جدید، رویه [Session_Start] اجرا می‌شود. یک سبد خرید خالی در جلسه مشتری ایجاد می‌شود – خطوط 78–81

1.5.7.2. کنترل‌کننده [main.aspx.vb]

کنترل‌کننده [main.aspx.vb] تمام درخواست‌های مشتری را مدیریت می‌کند. این به این دلیل است که همه آن‌ها به شکل [/webarticles/main.aspx?action=XX] هستند. یک درخواست به شرح زیر پردازش می‌شود:

  • شیء [erreurs] در زمینهٔ برنامه بررسی می‌شود. اگر خالی نباشد، این بدان معناست که در حین راه‌اندازی برنامه خطاهایی رخ داده و برنامه نمی‌تواند اجرا شود. در پاسخ، نما [ERREURS] ارسال می‌شود.
  • پارامتر [action] در درخواست بازیابی و بررسی می‌شود. اگر با هیچ اقدام شناخته‌شده‌ای مطابقت نداشته باشد، نمای [ERREURS] همراه با یک پیام خطای مناسب ارسال می‌شود.
  • اگر پارامتر [action] معتبر باشد، درخواست کلاینت برای پردازش به یک رویه مخصوص آن اقدام ارسال می‌شود:
روش
درخواست
پردازش
پاسخ‌های ممکن
doListe
GET /main?action=list
- درخواست فهرست اقلام
از کلاس تجاری
- نمایش آن
[LISTE] یا [ERREURS]
doInfos
GET /main?action=infos&id=ID
- مقاله با شناسه ID را از
کلاس بیزینس
- نمایش آن
[INFOS] یا [ERREURS]
doAchat
POST /main?action=purchase&id=ID
- مقدار خریداری‌شده در پارامترهای ارسال‌شده گنجانده شده است
- آیتم با شناسه ID را از
کلاس تجاری
- آن را در سبد خرید در
سشن مشتری
[LISTE] یا [INFOS] یا [ERREURS]
doRetirerAchat
GET /main?action=return&id=ID
- حذف آیتم با شناسه=ID از
فهرست خرید در
سشن مشتری
[PANIER]
doPanier
GET /main?action=basket
- نمایش
جلسه مشتری
[PANIER] یا [PANIERVIDE]
doValidationPanier
GET /main?action=validate-basket
- کاهش
سطح موجودی تمام اقلام
در مشتری
سشن مشتری
[LISTE] یا [ERREURS]

قالب کنترلر برای [main.aspx.vb] می‌تواند به شرح زیر باشد:

Imports System.Collections
Imports System
Imports System.Data

Imports istia.st.articles.dao
Imports istia.st.articles.domain

Namespace istia.st.articles.web

     'کلاس کنترل‌کنندهٔ برنامهٔ وب
    Public Class MainWebArticles
        Inherits System.Web.UI.Page

         ' میدان‌های خصوصی
        Private articlesDomain As IArticlesDomain
        Private options As Hashtable

         'بارگذاری صفحه
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
....
        End Sub

         ' متدهای رسیدگی به عملیات

         ' فهرست مقالات
        Public Sub doListe()
...
        End Sub

         'جزئیات محصول
        Public Sub doInfos()
...
        End Sub

         'خرید یک کالا
        Public Sub doAchat()
...
        End Sub

         ' حذف یک خرید
        Public Sub doRetirerAchat()
...
        End Sub

         'مشاهده سبد خرید
        Public Sub doPanier()
...
        End Sub

         ' پرداخت
        Public Sub doValidationPanier()
...
        End Sub

    End Class

End Namespace

توضیحات:

  • کلاس دارای دو فیلد خصوصی است که بین متدها مشترک خواهد بود – خطوط ۱۵–۱۶:
    • articlesDomain: کلاس تک‌نمونه (singleton) که دسترسی به لایه [domain] را فراهم می‌کند
    • options: آرایهٔ دیکشنری‌ها که حاوی گزینه‌های منو است
  • روند [Page_Load]:
    • دو فیلد خصوصی کلاس را مقداردهی اولیه می‌کند
    • پارامتر [action] را از درخواست بازیابی کرده و متدی را که این عمل را مدیریت می‌کند، اجرا می‌کند.

1.5.7.3. متد [Page_Load]

این اولین رویدادی است که در صفحه رخ می‌دهد. کد به شرح زیر است:

         ' بارگذاری صفحه
        Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
             'بررسی اینکه آیا برنامه به درستی شروع شده است
            Dim erreurs As ArrayList = CType(Application.Item("erreurs"), ArrayList)
             ' اگر خطایی وجود داشته باشد، نمای [erreurs] نمایش داده می‌شود
            If erreurs.Count <> 0 Then
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {}
                Server.Transfer(CType(Application("urlErreurs"), String))
            End If
             ' بازیابی شیء دسترسی برای کلاس کسب‌وکار
            articlesDomain = CType(Application.Item("articlesDomain"), IArticlesDomain)
             ' به همراه گزینه‌های منو
            options = CType(Application.Item("options"), Hashtable)
             ' عمل انجام‌دادنی را بازیابی می‌کند
            Dim action As String = Request.QueryString("action")
            If action Is Nothing Then
                action = "liste"
            End If
             ' اجرای اقدام
            Select Case action
                Case "liste"
                    doListe()
                Case "infos"
                    doInfos()
                Case "achat"
                    doAchat()
                Case "panier"
                    doPanier()
                Case "retirerachat"
                    doRetirerAchat()
                Case "validationpanier"
                    doValidationPanier()
                Case Else
                    doListe()
            End Select
        End Sub

توضیحات:

  • هر بار که صفحه بارگذاری می‌شود، ما اطمینان حاصل می‌کنیم که inicialization برنامه که توسط [global.asax] انجام شده، با موفقیت به پایان رسیده است.
  • برای این کار، فهرست خطاهایی را که توسط [global.asax] در زمینهٔ برنامه قرار داده شده است، بازیابی می‌کنیم – خط ۴
  • اگر این لیست خالی نبود، نمای [ERREURS] را نمایش می‌دهیم – خطوط ۶–۱۰
  • ما singleton [articlesDomain] را که توسط [global.asax] در زمینهٔ برنامه قرار داده شده است، بازیابی کرده و در فیلد خصوصی [articlesDomain] ذخیره می‌کنیم تا برای متدهای مختلف کلاس در دسترس باشد – خط ۱۲
  • ما یک فرآیند مشابه را با آرایه گزینه‌های منو انجام می‌دهیم – خط 14
  • پارامتر [action] از درخواست بازیابی می‌شود – خط 16
  • ما متد متناظر با اقدام درخواستی را اجرا می‌کنیم. یک اقدام غیرمنتظره به‌عنوان اقدام [liste] در نظر گرفته می‌شود – خطوط 16–36

1.5.7.4. پردازش اقدام [liste]

این شامل نمایش لیست اقلام است:

Image

کد به شرح زیر است:

         ' فهرست آیتم‌ها
        Public Sub doListe()
             ' پردازش خطا
            Dim erreurs As New ArrayList
             ' درخواست فهرست آیتم‌ها
            Dim articles As IList
            Try
                articles = articlesDomain.getAllArticles
            Catch ex As Exception
                 ' ثبت خطا
                erreurs.Add(ex.ToString)
            End Try
             ' آیا خطایی وجود دارد؟
            If erreurs.Count <> 0 Then
                 ' نمایش نما [erreurs]
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
            End If
             ' نمایش نما [liste]
            context.Items("articles") = articles
            context.Items("options") = New Hashtable() {CType(options("panier"), Hashtable)}
            If context.Items("message") Is Nothing Then context.Items("message") = ""
            Server.Transfer(CType(Application.Item("urlListe"), String))
        End Sub

توضیحات:

  • هرگونه خطا در [ArrayList] – خط ۴ ثبت می‌شود
  • فهرست آیتم‌ها از singleton [articlesDomain] – خطوط 5–12 درخواست می‌شود
  • اگر خطایی رخ داده باشد، نما [ERREURS] ارسال می‌شود – خطوط ۱۳–۱۹
  • در غیر این صورت، نما [LISTE] ارسال می‌شود – خطوط ۲۰–۲۴

1.5.7.5. پردازش اقدام [infos]

مشتری اطلاعاتی در مورد یک آیتم خاص درخواست کرده است:

Image

کد به شرح زیر است:

         ' اطلاعات محصول
        Public Sub doInfos()
             ' مدیریت خطا
            Dim erreurs As New ArrayList
             'بازیابی شناسه آیتم درخواست‌شده
            Dim strId As String = Request.QueryString("id")
             'آیا چیزی در دسترس است؟
            If strId Is Nothing Then
                 ' عادی نیست – صفحهٔ خطا را نمایش دهید
                erreurs.Add("action incorrecte (action=infos, id=rien)")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             'آیا یک عدد صحیح وجود دارد؟
            Dim id As Integer
            Try
                id = Integer.Parse(strId)
            Catch ex As Exception
                 ' غیرعادی – صفحهٔ خطا را ارسال کن
                erreurs.Add("action incorrecte (action=infos, id[" + strId + "] invalide)")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End Try
             ' درخواست آیتم با کلید id
            Dim unArticle As Article
            Try
                unArticle = articlesDomain.getArticleById(id)
            Catch ex As Exception
                 ' مشکل دسترسی به داده‌ها
                erreurs.Add("Problème d'accès aux données (" + ex.ToString + ")")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End Try
             'آیا موردی بازیابی شده است؟
            If unArticle Is Nothing Then
                 ' این آیتم وجود ندارد
                erreurs.Add("L'article d'id=" + id.ToString + " n'existe pas")
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             ' مورد بازیابی شده است – به جلسهٔ جاری اضافه می‌شود
            Session.Item("article") = unArticle
             ' آماده‌سازی برای نمایش آن
            context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
            context.Items("msg") = ""
            context.Items("qte") = ""
            Server.Transfer(CType(Application.Item("urlInfos"), String))
        End Sub

توضیحات:

  • هرگونه خطا در [ArrayList] – خط ۴ قرار می‌گیرد
  • شناسهٔ آیتم درخواستی از پرس‌وجو بازیابی می‌شود – خط ۶
  • این شناسه بررسی می‌شود. باید موجود باشد و یک عدد صحیح باشد. اگر اینطور نباشد، نما [ERREURS] با پیام خطای مناسب بازگردانده می‌شود – خطوط 7–27
  • پس از تأیید شناسه، آیتم از [articlesDomain] که یک شیء واحد (singleton) است، درخواست می‌شود. اگر استثنایی رخ دهد، نمای [ERREURS] بازگردانده می‌شود – خطوط 29–39
  • اگر آیتم یافت نشود، ویوی [ERREURS] بازگردانده می‌شود – خطوط ۴۱–۴۸
  • اگر آیتم پیدا شود، در جلسهٔ کاربر قرار می‌گیرد و سپس در نمای [INFOS] – خطوط ۵۰–۵۶ نمایش داده می‌شود.

1.5.7.6. پردازش اقدام [achat]

مشتری کالای نمایش‌داده‌شده در نما [INFOS] را خریداری کرده است.

Image

کد به شرح زیر است:

         'خرید یک آیتم
        Public Sub doAchat()
             'خرید یک آیتم
             'بازیابی آیتمی که در جلسه بود
            Dim unArticle As Article = CType(Session.Item("article"), Article)
             'آیا چیزی هست؟
            If unArticle Is Nothing Then
                 ' غیرعادی نیست – فهرست اقلام را نمایش دهید
                doListe()
            End If
             ' بازیابی مقدار ثبت‌شده
            Dim strQte As String = Request.Form("txtQte")
             'آیا چیزی وجود دارد؟
            If strQte Is Nothing Then
                 ' غیرطبیعی – ارسال لیست اقلام
                doListe()
            End If
             'آیا یک عدد صحیح وجود دارد؟
            Dim qte As Integer
            Try
                qte = Integer.Parse(strQte)
                If (qte <= 0) Then Throw New Exception
            Catch ex As Exception
                 ' غیرعادی – ارسال صفحه اطلاعات با پیام خطا
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                context.Items("msg") = "Quantité incorrecte"
                context.Items("qte") = strQte
                Server.Transfer(CType(Application.Item("urlInfos"), String))
            End Try
             'همه‌چیز خوب است – خرید را به سبد خرید مشتری اضافه کنید
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
            unPanier.ajouter(New Achat(unArticle, qte))
             ' نمایش فهرست اقلام
            doListe()
        End Sub

توضیحات:

  • مورد قرار داده شده در جلسه بازیابی می‌شود – خط ۵
  • اگر آن وجود نداشته باشد (ممکن است جلسه منقضی شده باشد)، نما [LISTE] نمایش داده می‌شود – خطوط ۷–۱۰
  • مقدار خریداری‌شده از پرس‌وجو بازیابی می‌شود – خط ۱۲
  • اعتبار آن بررسی می‌شود – خطوط ۱۳–۲۹
  • اگر نامعتبر باشد، بسته به شرایط، یا نما [LISTE] نمایش داده می‌شود – خط 16 یا نما [INFOS] – خطوط 24–28
  • اگر همه چیز مرتب باشد، خرید به سبد خرید اضافه می‌شود – خطوط ۳۱–۳۲
  • سپس view [LISTE] ارسال می‌شود – خط 34

1.5.7.7. پردازش اقدام [panier]

مشتری چندین خرید انجام داده و درخواست مشاهده سبد خرید را دارد:

Image

کد به شرح زیر است:

         ' مشاهده سبد خرید
        Public Sub doPanier()
             'سبد خرید را از جلسه بازیابی می‌کند
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' آیا سبد خالی است؟
            If unPanier.achats.Count = 0 Then
                 ' نمایش سبد خالی
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlPanierVide"), String))
            End If
             ' نمایش سبد غیرخالی
            context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable), CType(options("validationpanier"), Hashtable)}
            Server.Transfer(CType(Application.Item("urlPanier"), String))
        End Sub

توضیحات:

  • سبد خرید از جلسه بازیابی می‌شود – خط ۴. در اینجا بررسی نمی‌کنیم که آیا واقعاً چیزی بازیابی شده است یا خیر. باید این کار را انجام دهیم، زیرا ممکن است جلسه منقضی شده باشد.
  • اگر سبد خرید خالی باشد، نما [PANIERVIDE] ارسال می‌شود – خطوط ۶–۱۰
  • در غیر این صورت، نما [PANIER] بازگردانده می‌شود – خطوط ۱۱–۱۴

1.5.7.8. پردازش اقدام [retirerachat]

مشتری می‌خواهد یک کالا را از سبد خرید خود حذف کند:

Image

کد به شرح زیر است:

         ' حذف یک کالا از سبد خرید
        Public Sub doRetirerAchat()
             'بازیابی سبد از جلسه
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' حذف یک کالا
            Try
                 'بازیابی شناسهٔ آیتم حذف‌شده
                Dim idArticle As Integer = Integer.Parse(Request.QueryString("id"))
                 ' حذف آن از سبد خرید
                unPanier.enlever(idArticle)
            Catch ex As Exception
                 ' نمایش لیست اقلام
                doListe()
            End Try
             ' نمایش سبد خرید
            doPanier()
        End Sub

توضیحات:

  • سبد خرید از جلسه بازیابی می‌شود – خط ۴. در اینجا بررسی نمی‌کنیم که آیا واقعاً چیزی بازیابی شده است یا خیر. باید این کار را انجام دهیم، زیرا ممکن است جلسه منقضی شده باشد.
  • شناسه [id] آیتمی که باید حذف شود از درخواست دریافت می‌شود – خط ۸.
  • خرید متناظر از سبد خرید حذف می‌شود – خط ۱۰
  • اعتبار شناسه‌ی کالای خریداری‌شده در اینجا بررسی نشده است. اگر از نوع نامعتبر باشد، یک استثنا رخ خواهد داد و در خطوط ۱۱–۱۳ رسیدگی می‌شود. اگر معتبر باشد اما وجود نداشته باشد، متد [panier.enlever] – خط ۱۰ – هیچ کاری انجام نمی‌دهد.
  • سبد جدید نمایش داده می‌شود – خط 16

1.5.7.9. پردازش اقدام [validerpanier]

مشتری می‌خواهد سبد خرید خود را تأیید کند:

Image

کد به شرح زیر است:

         ' تسویه حساب
        Public Sub doValidationPanier()
             ' در ابتدا، هیچ خطایی وجود ندارد
            Dim erreurs As ArrayList
             'سبد خرید از جلسه بازیابی می‌شود
            Dim unPanier As Panier = CType(Session.Item("panier"), Panier)
             ' تلاش برای تأیید سبد خرید
            Try
                articlesDomain.acheter(unPanier)
                 ' هرگونه خطای خرید ثبت می‌شود
                erreurs = articlesDomain.erreurs
            Catch ex As Exception
                 ' ثبت خطا
                erreurs = New ArrayList
                erreurs.Add(String.Format("Erreur lors de la validation du panier [{0}]", ex.Message))
            End Try
             ' اگر خطاهایی وجود داشته باشد، صفحهٔ خطا را نمایش دهید
            If erreurs.Count <> 0 Then
                context.Items("erreurs") = erreurs
                context.Items("options") = New Hashtable() {CType(options("liste"), Hashtable), CType(options("panier"), Hashtable)}
                Server.Transfer(CType(Application.Item("urlErreurs"), String))
                Exit Sub
            End If
             ' همه چیز درست است – لیست اقلام را با یک پیام موفقیت نمایش دهید
            context.Items("message") = "Votre panier a été validé"
            doListe()
        End Sub

توضیحات:

  • سبد خرید از جلسه بازیابی می‌شود – خط ۶. در اینجا بررسی نمی‌کنیم که آیا واقعاً چیزی بازیابی شده است یا خیر. باید این کار را انجام دهیم، زیرا ممکن است جلسه منقضی شده باشد.
  • اگر جلسه منقضی شده باشد، ما یک اشاره‌گر [nothing] برای سبد خرید خواهیم داشت، و متد [acheter] – خط ۹ – یک استثنا پرتاب خواهد کرد و نما [ERREURS] بازگردانده خواهد شد. با این حال، پیام خطا برای کاربر چندان مبهم خواهد بود.
  • خطوط ۸–۱۶: ما سعی می‌کنیم سبد خرید بازیابی‌شده از جلسه را اعتبارسنجی کنیم. برخی خریدها ممکن است اعتبارسنجی نشوند اگر مقدار درخواستی از موجودی کالای موردنظر بیشتر باشد. این موارد توسط متد [acheter] در یک لیست خطا ثبت می‌شوند که در خط ۱۱ بازیابی می‌شود.
  • اگر خطایی وجود داشته باشد، نما [ERREURS] ارسال می‌شود – خطوط ۱۸–۲۳
  • در غیر این صورت، نما [LISTE] ارسال می‌شود – خطوط ۲۵–۲۶

1.6. Conclusion

در اینجا ما یک برنامه کاربردی مبتنی بر مدل MVC توسعه داده‌ایم. تا آوریل ۲۰۰۵، به نظر نمی‌رسد که هیچ چارچوب (framework) حرفه‌ای توسعه MVC در ASP.NET وجود داشته باشد، مانند آنهایی که در جاوا وجود دارند (Struts, Spring, ...). انتظار می‌رود پروژه [Spring.net] به‌زودی یکی از این چارچوب‌ها را ارائه دهد. تا زمان این توسعه، روش فوق امکان توسعهٔ قابل‌قبول MVC را برای برنامه‌های متوسط فراهم می‌کند.