I needed a way of attaching multiple repositories to a presenter. Given I already had an IRepository
abstraction, coming up with an IUnitOfWork
seemed the next logical step.
The code works, but I'm having issues with transactions because of MySQL (not sure if it's the driver or the underlying db engine, but everything just goes through as if there was no transaction involved) - but that's irrelevant to the actual code - I'm sure I can hook this up to a SQL Server backend and it will work just as expected.
The UoW opens a connection upon being created, and initiates a transaction too. Because I wanted the client code to be able to commit multiple transactions using the same UoW instance, I'm initiating a new transaction every time I Commit
or Rollback
.
So here it is:
IUnitOfWork class module
Option Explicit
Public Sub AddRepository(repo As IRepository)
End Sub
Public Property Get Repository(ByVal key As String) As IRepository
End Property
Public Sub Commit()
End Sub
Public Sub Rollback()
End Sub
UnitOfWork class module
Option Explicit
Private Const CONNECTION_STRING As String = "DRIVER={MySQL ODBC 5.1 Driver};UID=;PWD=;SERVER=;DATABASE=;PORT=;"
Private repositories As New Dictionary
Private adoConnection As New ADODB.Connection
Private cmd As New SqlCommand
Implements IUnitOfWork
Private Sub Class_Initialize()
adoConnection.ConnectionString = CONNECTION_STRING
adoConnection.Open
adoConnection.BeginTrans
End Sub
Private Sub Class_Terminate()
On Error GoTo ErrHandler
Set repositories = Nothing
If Not adoConnection Is Nothing Then
If adoConnection.State = adStateOpen Then
adoConnection.Close
End If
Set adoConnection = Nothing
End If
Exit Sub
ErrHandler:
Err.Raise Err.number, Err.source, Err.description, Err.HelpFile, Err.HelpContext
End Sub
Public Sub AddRepository(ByVal key As String, ByRef repo As IRepository)
repo.SetConnection adoConnection
repositories.Add key, repo
End Sub
Public Property Get Repository(ByVal key As String) As IRepository
Set Repository = repositories(key)
End Property
Public Sub Commit()
On Error GoTo ErrHandler
adoConnection.CommitTrans
adoConnection.BeginTrans
Exit Sub
ErrHandler:
Err.Raise Err.number, Err.source, Err.description, Err.HelpFile, Err.HelpContext
End Sub
Public Sub Rollback()
On Error GoTo ErrHandler
adoConnection.RollbackTrans
adoConnection.BeginTrans
Exit Sub
ErrHandler:
Err.Raise Err.number, Err.source, Err.description, Err.HelpFile, Err.HelpContext
End Sub
Private Sub IUnitOfWork_AddRepository(ByVal key As String, ByRef repo As IRepository)
AddRepository key, repo
End Sub
Private Sub IUnitOfWork_Commit()
Commit
End Sub
Private Property Get IUnitOfWork_Repository(ByVal key As String) As IRepository
Set IUnitOfWork_Repository = Repository(key)
End Property
Private Sub IUnitOfWork_Rollback()
Rollback
End Sub
Here's how this code helped solve the presenter-that-isn't-a-presenter problem - I simply add the two repositories I need, and the presenter can call methods on both UnitOfWork.Repository("ExcludedOrders")
and UnitOfWork.Repository("Orders")
, so I could get rid of the OrderHeaderPresenter
empty shell:
Public Sub MaintainExcludedOrders()
On Error GoTo ErrHandler
If IsBusy Then Exit Sub
Dim orders As New OrderHeaderRepository
Dim exclOrders As New ExcludedOrdersRepository
Dim uow As New UnitOfWork
uow.AddRepository "ExcludedOrders", exclOrders
uow.AddRepository "Orders", orders
Dim presenter As New ExcludedOrdersPresenter
Set presenter.UnitOfWork = uow
SetupSimplePresenter presenter, _
GetResourceString("ExcludedOrdersTitle"), _
GetResourceString("ExcludedOrdersInstructionsText"), _
CRUD
presenter.Show
CleanExit:
Set uow = Nothing
Set presenter = Nothing
Exit Sub
ErrHandler:
MsgBox Err.description, vbCritical, GENERIC_ERR_MSG
Resume CleanExit
End Sub