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:
Public Interface ICoupon
Inherits CouponService.IEditableObject
Enum ExpirationState As Integer
NotExpired = 0
Expired = 1
End Enum
ReadOnly Property CouponIdentification As Integer
Property CouponName As String
Property ExpirationDate As DateTime
ReadOnly Property ExpirationType As ExpirationState
Property QuantityOfProductsRequired As Integer
Property QuantityOfProductsReduced As Integer
Property Percentage As Nullable(Of Integer)
Property Amount As Nullable(Of Double)
Property AvailableProductsRequired As IList(Of IProduct)
Property AvailableProductsReduced As IList(Of IProduct)
Property StorageInformation As String
Property RequiredStore As IStore
Sub GetCouponsByExpirationState(state As ExpirationState, _
callback As Action(Of IList(Of ICoupon)))
End Interface
The data access layer:
Imports CouponService
Imports CouponService.Naming
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.
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
Protected Overridable Sub Dispose(disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
End If
End If
Me.disposedValue = True
End Sub
Public Sub Dispose() Implements IDisposable.Dispose
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.