I was just looking for some feedback on what others are doing to manage their context when using EF4. I am creating an MVC app, using (I think :)) the Unit of Work, Service Layer, Repository, EF4 w/POCO technique.
My Controllers utilize services which take a UOW, which then utilize repositories to get POCO's from the EF.
Is this implemented correctly?
Have a look below, and any feedback is appreciated!!
Controller
Public Class MyController
Function ListCustomers() As ActionResult
Dim _UOW = New UnitOfWork
Dim _Service = New CustomerService(_UOW)
Dim _Model = New CustomersViewModel
_Model.Customers = _Service.GetCustomers
_UOW.Dispose()
Return View(_Model)
End Function
End Class
Unit of Work
Public Interface IUnitOfWork
Property Context As GTGContext
Sub Committ()
End Interface
Public Class UnitOfWork
Implements IDisposable, IUnitOfWork
Public Property Context As Domain.GTGContext Implements IUnitOfWork.Context
Public Sub New()
_Context = New GTGContext
End Sub
Public Sub Committ() Implements IUnitOfWork.Committ
_Context.SaveChanges()
End Sub
#Region "IDisposable Support"
Private _IsDisposed As Boolean
Protected Overridable Sub Dispose(ByVal Disposing As Boolean)
If (Not _IsDisposed) Then
If (Disposing) Then
If (_Context IsNot Nothing) Then
_Context.Dispose()
End If
End If
'TODO: Free unmanaged resources (unmanaged objects) and override Finalize() below.
End If
_IsDisposed = True
End Sub
'TODO: Override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
'Protected Overrides Sub Finalize()
' Dispose(False)
' MyBase.Finalize()
'End Sub
Public Sub Dispose() Implements IDisposable.Dispose
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
#End Region
End Class
Service
Public Class CustomerService
Private _UOW As IUnitOfWork
Private _Repo As Repository(Of Customer)
Public Sub New(UOW As IUnitOfWork)
_UOW = UOW
_Repo = New Repository(Of Customer)(_UOW)
End Sub
Public Function GetCustoemrs() As IQueryable(Of Customer)
' Any Business Logic Here
Return _Repo.GetCustomers()
End Function
End Class
Repository
Imports System.Data.Objects
Namespace Repositories
Public Interface IRepository(Of T As Class)
ReadOnly Property ObjectSet As IObjectSet(Of T)
ReadOnly Property UnitOfWork As IUnitOfWork
Function Query(ByVal Expression As Expressions.Expression(Of Func(Of T, Boolean))) As IQueryable(Of T)
Function GetFirst(ByVal Expression As Expressions.Expression(Of Func(Of T, Boolean))) As T
Function GetSingle(ByVal Expression As Expressions.Expression(Of Func(Of T, Boolean))) As T
Sub Add(ByVal Entity As T)
Sub Attach(ByVal Entity As T)
Sub Delete(ByVal Entity As T)
Sub SaveChanges()
End Interface
Public Class Repository(Of T As Class)
Implements IRepository(Of T)
#Region "Private Members/Properties"
Private _ObjectSet As IObjectSet(Of T)
Private ReadOnly Property ObjectSet As System.Data.Objects.IObjectSet(Of T) Implements IRepository(Of T).ObjectSet
Get
If (_ObjectSet Is Nothing) Then
_ObjectSet = UnitOfWork.Context.CreateObjectSet(Of T)()
End If
Return _ObjectSet
End Get
End Property
Private _UnitOfWork As IUnitOfWork
Private ReadOnly Property UnitOfWork As IUnitOfWork Implements IRepository(Of T).UnitOfWork
Get
Return _UnitOfWork
End Get
End Property
#End Region
#Region "Constructor(s)"
Public Sub New(ByVal UnitOfWork As IUnitOfWork)
If (UnitOfWork Is Nothing) Then
Throw New ArgumentNullException("UnitOfWork")
End If
_UnitOfWork = UnitOfWork
End Sub
#End Region
#Region "IRepository(Of T)"
Public Sub Add(ByVal Entity As T) Implements IRepository(Of T).Add
ObjectSet.AddObject(Entity)
End Sub
Public Sub Attach(ByVal Entity As T) Implements IRepository(Of T).Attach
ObjectSet.Attach(Entity)
UnitOfWork.Context.ObjectStateManager.ChangeObjectState(Entity, EntityState.Modified)
End Sub
Public Sub Delete(ByVal Entity As T) Implements IRepository(Of T).Delete
ObjectSet.DeleteObject(Entity)
End Sub
Public Function GetFirst(ByVal Expression As System.Linq.Expressions.Expression(Of System.Func(Of T, Boolean))) As T Implements IRepository(Of T).GetFirst
Return ObjectSet.FirstOrDefault(Expression)
End Function
Public Function GetSingle(ByVal Expression As System.Linq.Expressions.Expression(Of System.Func(Of T, Boolean))) As T Implements IRepository(Of T).GetSingle
Return ObjectSet.SingleOrDefault(Expression)
End Function
Public Function Query(ByVal Expression As System.Linq.Expressions.Expression(Of System.Func(Of T, Boolean))) As System.Linq.IQueryable(Of T) Implements IRepository(Of T).Query
Return ObjectSet.Where(Expression)
End Function
Public Sub SaveChanges() Implements IRepository(Of T).SaveChanges
UnitOfWork.Committ()
End Sub
#End Region
End Class
End Namespace
Let me tell you, I agonised over what approach to take for a long time, mostly on StackOverflow :)
I settled on an implementation of this fine article on managing the object context (take note of the ammended version allowing for multiple contexts).
http://dotnetslackers.com/articles/ado_net/Managing-Entity-Framework-ObjectContext-lifespan-and-scope-in-n-layered-ASP-NET-applications.aspx
Then I implemented facades like:
Sure, it's old but boy oh boy, it works!
See, the thing with all that service layer / unit of work / repository stuff is you end up having to write a hell of a lot of code just to support it (and surely EF4 was supposed to reduce code bloat!). Every time you add a new entity, guess what? Even more tedious code bloat.
With the method above, I have all the facade / unit of work support code stuffed into a reusable class library and all I have to do is write my facade code, which is ONLY concerned with code that actually does something - and it works with multiple contexts.
Works for me... ooh and don't lambast me on the use of the word 'facade'. I never really went to school ;)
Thanks, Richard
P.s. Have a great weekend!