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:
Code Snippet
- 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:
Code Snippet
- 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.
Code Snippet
- 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.
No comments:
Post a Comment