Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Converted 4-Tier Architecture

0.00/5 (No votes)
7 May 2013CPOL1 min read 6.8K  
Converted 4-Tier architecture.

This is a continuation article. The original article can be found here.

The converted 4-tier architecture is about having flexible views and data access layers. The view is simply a group of concrete classes that all implement something that is referenced in the presentation layer. The presentation layer instantiates the concrete views (from a different assembly) by using the IOC container.

In the presentation layer we'd create our IoC. The presentation layer references the business logic layer, so it can see all of the interfaces. So when our presentation layer needs an implementation of ICoupon, the IoC will retrieve the data access layers' implementation that is in a different assembly.

From Business Logic Layer:

VB
Public Interface ICoupon
    Inherits CouponService.IEditableObject
 
    ''' <summary>
    ''' Simple on and off states that allow simpler searches.
    ''' </summary>
    ''' <remarks>Instead of being required to validate every date in an open setting, this allows 
    ''' the class itself to handle that interpretation.</remarks>
    Enum ExpirationState As Integer
        NotExpired = 0
        Expired = 1
    End Enum
 
    ''' <summary>
    ''' Gets the coupon id.
    ''' </summary>
    ''' <value>
    ''' The coupon id.
    ''' </value>
    ''' <remarks>This is primarily for the database.</remarks>
    ReadOnly Property CouponIdentification As Integer
    ''' <summary>
    ''' Gets or sets the item.
    ''' </summary>
    ''' <value>
    ''' The item.
    ''' </value>
    ''' <remarks>The item property is simply a name given by the person who has the coupon.  This
    ''' name doesn't have to be used and is purely for assistance
    ''' in searching for specific coupons.</remarks>
    Property CouponName As String
    ''' <summary>
    ''' Gets or sets the expiration.
    ''' </summary>
    ''' <value>
    ''' The expiration.
    ''' </value>
    ''' <remarks>This is a set expiration date for the coupon.</remarks>
    Property ExpirationDate As DateTime
    ''' <summary>
    ''' Gets the type of the expiration.
    ''' </summary>
    ''' <value>
    ''' The type of the expiration.
    ''' </value>
    ''' <remarks>The expiration type has been added to allow a quick retrieval of getting a coupon
    ''' that has or has not been expired.</remarks>
    ReadOnly Property ExpirationType As ExpirationState
 
    ''' <summary>
    ''' Gets or sets the quantity products required.
    ''' </summary>
    ''' <value>
    ''' The quantity products required.
    ''' </value>
    ''' <remarks>This is the number of products that are required before the reduced price or the
    ''' reduced percentage can take into affect on the transaction.</remarks>
    Property QuantityOfProductsRequired As Integer
    ''' <summary>
    ''' Gets or sets the quantity products reduced.
    ''' </summary>
    ''' <value>
    ''' The quantity products reduced.
    ''' </value>
    ''' <remarks>The quantity of products that are
    ''' allowed to be reduced in price.</remarks>
    Property QuantityOfProductsReduced As Integer
    ''' <summary>
    ''' Gets or sets at percentage.
    ''' </summary>
    ''' <value>
    ''' At percentage.
    ''' </value>
    ''' <remarks>The percentage of the reduction of price.</remarks>
    Property Percentage As Nullable(Of Integer)
    ''' <summary>
    ''' Gets or sets at amount.
    ''' </summary>
    ''' <value>
    ''' At amount.
    ''' </value>
    ''' <remarks>The amount of the reduction in price.</remarks>
    Property Amount As Nullable(Of Double)
 
    ''' <summary>
    ''' Gets or sets the products required.
    ''' </summary>
    ''' <value>
    ''' The products required.
    ''' </value>
    ''' <remarks>A list of products in which one or more may be required to have been bought in
    ''' order to reduce the price.</remarks>
    Property AvailableProductsRequired As IList(Of IProduct)
    ''' <summary>
    ''' Gets or sets the products reduced.
    ''' </summary>
    ''' <value>
    ''' The products reduced.
    ''' </value>
    ''' <remarks>A list of products in which the price
    ''' may drop for.  The product has to be bought
    ''' for the coupon to take effect.</remarks>
    Property AvailableProductsReduced As IList(Of IProduct)
 
    ''' <summary>
    ''' Gets or sets the storage code.
    ''' </summary>
    ''' <value>
    ''' The storage code.
    ''' </value>
    ''' <remarks>This is a way of allowing the end users
    ''' to write a storage code or number.</remarks>
    Property StorageInformation As String
    ''' <summary>
    ''' Gets or sets the required store.
    ''' </summary>
    ''' <value>
    ''' The required store.
    ''' </value>
    ''' <remarks>Some coupons work only at certain stores.
    ''' This may be a requirement</remarks>
    Property RequiredStore As IStore
 
    Sub GetCouponsByExpirationState(state As ExpirationState, _
                callback As Action(Of IList(Of ICoupon)))
End Interface

The data access layer:

VB
Imports CouponService
Imports CouponService.Naming
 
''' <summary>
''' 
''' </summary>
Public Class Coupon
    Implements CouponService.ICoupon, CouponService.IEditableObject, _
      System.ComponentModel.INotifyPropertyChanged, _
      System.ComponentModel.INotifyPropertyChanging
 
    Public Event IsNowClean(sender As Object, e As System.EventArgs) _
      Implements CouponService.IEditableObject.IsNowClean
    Public Event IsNowDirty(sender As Object, e As System.EventArgs) _
      Implements CouponService.IEditableObject.IsNowDirty
    Public Event PropertyChanged(sender As Object, _
      e As System.ComponentModel.PropertyChangedEventArgs) _
      Implements System.ComponentModel.INotifyPropertyChanged.PropertyChanged
    Public Event PropertyChanging(sender As Object, _
      e As System.ComponentModel.PropertyChangingEventArgs) _
      Implements System.ComponentModel.INotifyPropertyChanging.PropertyChanging
 
    Private _IsDirty As Boolean
    Private _IsNew As Boolean
    Private _IsEditable As Boolean
 
    Private CouponContext As CouponContext
 
    Public Sub New(dataContext As Data.Linq.DataContext)
        CouponContext = dataContext
        _IsEditable = True
        _IsDirty = False
        _IsNew = True
    End Sub
 
#Region "IEditableObject Properties"
    Public ReadOnly Property IsDirty1 As Boolean Implements _
              CouponService.IEditableObject.IsDirty
        Get
            Return _IsDirty
        End Get
    End Property
    Public Property IsDirty As Boolean
        Get
            Return _IsDirty
        End Get
        Set(value As Boolean)
            If _IsDirty <> value Then
                _IsDirty = value
                If value Then
                    RaiseEvent IsNowClean(Me, EventArgs.Empty)
                Else
                    RaiseEvent IsNowDirty(Me, EventArgs.Empty)
                End If
            End If
        End Set
    End Property
 
    Public ReadOnly Property IsEditable1 As Boolean Implements _
            CouponService.IEditableObject.IsEditable
        Get
            Return _IsEditable
        End Get
    End Property
    Public Property IsEditable As Boolean
        Get
            Return _IsEditable
        End Get
        Set(value As Boolean)
            _IsEditable = value
        End Set
    End Property
 
    Public ReadOnly Property IsNew1 As Boolean Implements _
             CouponService.IEditableObject.IsNew
        Get
            Return _IsNew
        End Get
    End Property
    Public Property IsNew As Boolean
        Get
            Return _IsNew
        End Get
        Set(value As Boolean)
            _IsNew = value
        End Set
    End Property
#End Region
 
    Public ReadOnly Property CouponIdentification As Integer _
             Implements CouponService.ICoupon.CouponIdentification
        Get
            Return Me.CouponID
        End Get
    End Property
 
    Public Property ExpirationDate As Date Implements CouponService.ICoupon.ExpirationDate
        Get
            Return Me.Expiration
        End Get
        Set(value As Date)
            If value <> Me.ExpirationDate AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() ExpirationDate))
                Me.Expiration = value
                HasChanged(GetPropertyName(Function() ExpirationDate))
            End If
        End Set
    End Property
 
    Public ReadOnly Property ExpirationType As _
      CouponService.ICoupon.ExpirationState Implements CouponService.ICoupon.ExpirationType
        Get
            If Me.ExpirationDate < Now Then
                Return CouponService.ICoupon.ExpirationState.NotExpired
            Else
                Return CouponService.ICoupon.ExpirationState.Expired
            End If
        End Get
    End Property
 
    Public Property CouponName As String Implements CouponService.ICoupon.CouponName
        Get
            Return Me.Item
        End Get
        Set(value As String)
            If value <> Me.Item AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() CouponName))
                Me.Item = value
                HasChanged(GetPropertyName(Function() CouponName))
            End If
        End Set
    End Property
 
    Public Property StorageInformation As String Implements _
              CouponService.ICoupon.StorageInformation
        Get
            Return Me.StorageCode
        End Get
        Set(value As String)
            If value <> Me.StorageCode AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() StorageInformation))
                Me.StorageCode = value
                HasChanged(GetPropertyName(Function() StorageInformation))
            End If
        End Set
    End Property
 
    Public Property RequiredStore As CouponService.IStore _
             Implements CouponService.ICoupon.RequiredStore
        Get
            Return Me.Store
        End Get
        Set(value As CouponService.IStore)
            If value.StoreId <> Me.Store.StoreID AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() RequiredStore))
                Me.Store = value
                HasChanged(GetPropertyName(Function() RequiredStore))
            End If
        End Set
    End Property
 
    Public Property Amount As Double? Implements CouponService.ICoupon.Amount
        Get
            Return Me.AtAmount
        End Get
        Set(value As Double?)
            If value <> Me.AtAmount AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() Amount))
                Me.AtAmount = value
                HasChanged(GetPropertyName(Function() Amount))
            End If
        End Set
    End Property
 
    Public Property Percentage As Integer? Implements CouponService.ICoupon.Percentage
        Get
            Return Me.AtPercentage
        End Get
        Set(value As Integer?)
            If value <> Me.AtPercentage AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() Percentage))
                Me.AtPercentage = value
                HasChanged(GetPropertyName(Function() Percentage))
            End If
        End Set
    End Property
 
    Public Property AvailableProductsReduced As System.Collections.Generic.IList(Of _
            CouponService.IProduct) Implements CouponService.ICoupon.AvailableProductsReduced
        Get
            Return Me.ProductsReduced.ToList
        End Get
        Set(value As System.Collections.Generic.IList(Of CouponService.IProduct))
            If IsEditable Then
                IsChanging(GetPropertyName(Function() AvailableProductsReduced))
                Me.ProductsReduced.Clear()
 
                For Each prod As CouponService.IProduct In value
                    Me.ProductsReduced.Add(prod)
                Next
                HasChanged(GetPropertyName(Function() AvailableProductsReduced))
            End If
        End Set
    End Property
 
    Public Property AvailableProductsRequired As System.Collections.Generic.IList(Of _
            CouponService.IProduct) Implements CouponService.ICoupon.AvailableProductsRequired
        Get
            Dim ProductsToReturn As IEnumerable(Of IProduct) = _
                     From p In Me.ProductsRequired Select p.Product
            Return ProductsToReturn.ToList
        End Get
        Set(value As System.Collections.Generic.IList(Of CouponService.IProduct))
            If IsEditable Then
                IsChanging(GetPropertyName(Function() AvailableProductsRequired))
                Me.ProductsRequired.Clear()
 
                For Each prod As CouponService.IProduct In value
                    Me.ProductsRequired.Add(prod)
                Next
                HasChanged(GetPropertyName(Function() AvailableProductsRequired))
            End If
        End Set
    End Property
 
    Public Property QuantityOfProductsReduced As Integer Implements _
                CouponService.ICoupon.QuantityOfProductsReduced
        Get
            Return Me.QuantityProductsReduced
        End Get
        Set(value As Integer)
            If value <> Me.QuantityProductsReduced AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() QuantityOfProductsReduced))
                Me.QuantityProductsReduced = value
                HasChanged(GetPropertyName(Function() QuantityOfProductsReduced))
            End If
        End Set
    End Property
 
    Public Property QuantityOfProductsRequired As Integer Implements _
               CouponService.ICoupon.QuantityOfProductsRequired
        Get
            Return Me.QuantityProductsRequired
        End Get
        Set(value As Integer)
            If value <> Me.QuantityProductsRequired AndAlso IsEditable Then
                IsChanging(GetPropertyName(Function() QuantityOfProductsRequired))
                Me.QuantityProductsRequired = value
                HasChanged(GetPropertyName(Function() QuantityOfProductsRequired))
            End If
        End Set
    End Property
 
    Private Sub IsChanging(propertyName As String)
        RaiseEvent PropertyChanging(Me, _
          New System.ComponentModel.PropertyChangingEventArgs(propertyName))
    End Sub
    Private Sub HasChanged(propertyName As String)
        If IsEditable Then
            IsDirty = True
        End If
 
        RaiseEvent PropertyChanged(Me, _
           New System.ComponentModel.PropertyChangedEventArgs(propertyName))
    End Sub
 
    Public Sub GetCouponsByExpirationState(state As CouponService.ICoupon.ExpirationState, _
           callback As Action(Of IList(Of ICoupon))) Implements _
           CouponService.ICoupon.GetCouponsByExpirationState
        If state = ICoupon.ExpirationState.Expired Then
            callback.Invoke(GetExpiredCoupons)
        Else
            callback.Invoke(GetNonExpiredCoupons)
        End If
    End Sub
    Friend Function GetExpiredCoupons() As IList(Of ICoupon)
        Dim CouponsToReturn As IEnumerable(Of Coupon) = _
            From CurrentCoupon As Coupon In CouponContext.GetTable(Of Coupon)() _
            Where CurrentCoupon.Expiration < Now _
            Select CurrentCoupon

        Return MarkAsEditable(CouponsToReturn).ToList
    End Function
    Friend Function GetNonExpiredCoupons() As IList(Of ICoupon)
        Dim CouponsToReturn As IEnumerable(Of Coupon) = _
            From CurrentCoupon As Coupon In CouponContext.GetTable(Of Coupon)() _
            Where CurrentCoupon.Expiration >= Now _
            Select CurrentCoupon
 
        Return MarkAsEditable(CouponsToReturn).ToList
    End Function
 
    Private Function MarkAsEditable(ByVal CouponsToMark _
            As IEnumerable(Of ICoupon)) As IEnumerable(Of ICoupon)
        For Each CurrentCoupon As Coupon In CouponsToMark
            CurrentCoupon.IsNew = False
            CurrentCoupon.IsEditable = True
            CurrentCoupon.IsDirty = False
        Next
 
        Return CouponsToMark
    End Function
End Class

Now this coupon class is a partial class that was generated using Linq2Sql. In other words, my presentation layer is referencing the business logic layers' ICoupon interface which is implemented in the data access layer which is the same as my Linq2Sql entity. I pass in a data context to the constructor. Now this data context concrete class is also in the Data access layer and it implements the interface IDataAccess which is in my original article.

Here is the concrete Data Access class.

VB
Public Class DataAccess
    Implements CouponService.IDataAccess
 
    Private _DataContext As CouponContext
 
    Public Sub New(connectionString As String)
        _DataContext = New CouponContext(connectionString)
    End Sub
 
    Public ReadOnly Property GetDataContext As System.Data.Linq.DataContext _
               Implements CouponService.IDataAccess.GetDataContext
        Get
            Return _DataContext
        End Get
    End Property
    Public Property Timeout As Integer Implements CouponService.IDataAccess.Timeout
        Get
            Return _DataContext.CommandTimeout
        End Get
        Set(value As Integer)
            _DataContext.CommandTimeout = value
        End Set
    End Property
 
    Public Function Commit() As Boolean Implements CouponService.IDataAccess.Commit
        Try
            _DataContext.SubmitChanges()
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function
    Public Function Commit(mode As System.Data.Linq.ConflictMode) As _
                    Boolean Implements CouponService.IDataAccess.Commit
        Try
            _DataContext.SubmitChanges()
            Return True
        Catch ex As Exception
            Return False
        End Try
    End Function
 
    Public Function ConnectionState() As System.Data.ConnectionState _
                    Implements CouponService.IDataAccess.ConnectionState
        Return _DataContext.Connection.State
    End Function
    Public Function OpenConnection() As Boolean Implements CouponService.IDataAccess.OpenConnection
        _DataContext.Connection.Open()
    End Function
 
    Public Sub Rollback() Implements CouponService.IDataAccess.Rollback
        For Each change As Object In _DataContext.GetChangeSet.Updates
            _DataContext.Refresh(Data.Linq.RefreshMode.OverwriteCurrentValues, change)
        Next
        For Each change As Object In _DataContext.GetChangeSet.Inserts
            _DataContext.Refresh(Data.Linq.RefreshMode.OverwriteCurrentValues, change)
        Next
        For Each change As Object In _DataContext.GetChangeSet.Deletes
            _DataContext.Refresh(Data.Linq.RefreshMode.OverwriteCurrentValues, change)
        Next
    End Sub
 
#Region "IDisposable Support"
    Private disposedValue As Boolean ' To detect redundant calls
 
    ' IDisposable
    Protected Overridable Sub Dispose(disposing As Boolean)
        If Not Me.disposedValue Then
            If disposing Then
                ' TODO: dispose managed state (managed objects).
            End If
 
            ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
            ' TODO: set large fields to null.
        End If
        Me.disposedValue = True
    End Sub
 
    ' TODO: override Finalize() only if Dispose(ByVal disposing
    ' As Boolean) above has code to free unmanaged resources.
    'Protected Overrides Sub Finalize()
    '    ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    '    Dispose(False)
    '    MyBase.Finalize()
    'End Sub
 
    ' This code added by Visual Basic to correctly implement the disposable pattern.
    Public Sub Dispose() Implements IDisposable.Dispose
        ' Do not change this code.  Put cleanup code in Dispose(ByVal disposing As Boolean) above.
        Dispose(True)
        GC.SuppressFinalize(Me)
    End Sub
#End Region
End Class

Eventually, I will be putting the whole application online when it's completed. If you have any questions please feel free to ask. I have a total of 6 assemblies. 2 of them are for automated tests. I have 1 that is testing the presentation layer that mocks the views and the DAL. My other one tests the concrete DAL classes.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)