2

Using the unit of work and repository patterns, I have the following:

public interface IDatabaseFactory<C> : IDisposable
{
    C Get();
    void Set(string connectionString);
}

public interface IUnitOfWork<C>
{
    ICustomerRepository Customers { get; }
    //etc...

    void Commit();
    void ChangeDatabase(string connectionString);
    string GetConnectionString(Database database);
}

public interface ICustomerRepository : IRepository<Customer> { }

And the implementation:

public class CustomerRepository : Repository<MyDbContext, Customer>, ICustomerRepository
{
    public CustomerRepository (IDatabaseFactory<MyDbContext> databaseFactory) 
        : base(databaseFactory) { }
}

public class UnitOfWork<C> : IUnitOfWork<C> where C : DbContext, IMyDbContext
{
    private readonly IDatabaseFactory<C> databaseFactory;

    private C dataContext;

    public UnitOfWork(IDatabaseFactory<C> databaseFactory)
    {
        this.databaseFactory = databaseFactory;
    }

    private ICustomerRepository customerRepository;
    public ICustomerRepository Customers
    {
        get { return customerRepository?? (customerRepository = new CustomerRepository((IDatabaseFactory<MyDbContext>)databaseFactory)); }
    }

    private void ClearRespositories()
    {
        customerRepository = null;
    }

    protected C DataContext
    {
        get { return dataContext ?? (dataContext = databaseFactory.Get()); }
    }

    public string GetConnectionString(Model.Database database)
    {
    }

    public void ChangeDatabase(string connectionString)
    {
    }

    public void Commit()
    {
    }
}

My model has no less and 250 main entities which, using the above pattern, require the same amout of repositories, and each and every one is a property in the unit of work class.

My typical service layer class is:

public class CustomerService : BaseService, ICustomerService
{
    private ICustomerRepository customerRepository;

    public CustomerService(IUnitOfWork<MyDbContext> unitOfWork) : base(unitOfWork) { }

    public override void ChangeDatabase(string connectionString)
    {
        base.ChangeDatabase(connectionString);
        customerRepository = unitOfWork.Customers;
    }
}

My application has one db context, and this context is used across for different databases (using the same context). When a user logs on the system, he can select a database and this will in turn set the data context of the uow class accordingly.

I've added the repositories to the UoW class so that all repositories have the same uow.

Is this the correct way to do it or is there a better way?

Ivan-Mark Debono
  • 15,500
  • 29
  • 132
  • 263
  • Consider different approach, using generic repositories doesn't have to be a good way, see domain driven design and http://stackoverflow.com/questions/14032834/generic-vs-individual-repository-for-aggregate-root – Robert Goldwein Mar 21 '14 at 08:17
  • _"My application has one db context"_ Do you mean you do not dispose your context once in the lifetime of your application? That is not a smart idea. – Loetn Mar 21 '14 at 08:17
  • Ever considered not to use these [redundant layers](http://stackoverflow.com/q/5625746/861716)? – Gert Arnold Mar 21 '14 at 11:13

1 Answers1

0

You can use a generic repository pattern. And this can be extended with very specific domain queries. Your DI container can then create the specific repository you require and you can work against it for all dataclasses.

If any questions about the code feel free to ask.

Im' gonna give you my VB.net code, hope it is usefull:

This is the interface you'll be working to. Saving and retrieving your Classes (They Must inherit from IEntity)

Public Interface IRepository
    Function GetEntity(Of T As {EntityObject, IEntity})(id As Integer) As T

    Function GetAll(Of T As {EntityObject, IEntity})() As IEnumerable(Of T)

    Sub Add(Of T As {EntityObject, IEntity})(entity As T)
    Sub Remove(Of T As EntityObject)(entity As T)
    Sub Attach(Of T As EntityObject)(entity As T)
    Sub Update(Of T As {EntityObject, IEntity})(entity As T)

    Function ExecuteQuery(Of T As {EntityObject, IEntity})(query As IQuery(Of T)) As IQueryResult(Of T)
    Function ExecuteQuery(Of T As {EntityObject, IEntity}, TResult)(query As IQuery(Of T, TResult)) As IQueryResult(Of TResult)
End Interface

The interface you must implement in your data objects You can also use other then int for your PK:

Public Interface IEntity
    Property Id As Int32
End Interface

The IQuery object, implement this for a specific query on your data:

Public Interface IQuery(Of T As {EntityObject, IEntity})
    Function Execute(ByVal objectSet As ObjectSet(Of T)) As IQueryResult(Of T)
End Interface

Public Interface IQuery(Of T As {EntityObject, IEntity}, TResult)
    Function Execute(ByVal objectSet As ObjectSet(Of T)) As IQueryResult(Of TResult)
End Interface

The QueryResult you'll be working to, and it's extendable

Public Interface IQueryResult(Of T)
    Function UniqueResult() As T
    Function List() As IEnumerable(Of T)
End Interface

Public Class LinqResult(Of T)
    Implements IQueryResult(Of T)
    Private ReadOnly _query As IQueryable(Of T)

    Public Sub New(query As IQueryable(Of T))
        _query = query
    End Sub

    Protected Overridable Function ExpandQuery(query As IQueryable(Of T)) As IQueryable(Of T)
        Return query
    End Function

    Public Function UniqueResult() As T Implements IQueryResult(Of T).UniqueResult
        Dim result = ExpandQuery(_query).SingleOrDefault()
        If result Is Nothing Then
            Throw New EntityNotFoundException(Of T)()
        End If
        Return result
    End Function
    Public Function List() As IEnumerable(Of T) Implements IQueryResult(Of T).List
        Return ExpandQuery(_query).ToList()
    End Function
End Class

Your ObjectcontextRepository, the actual implementation of your IRepository

Public Class EntityObjectContextRepository
    Implements IRepository

    Private ReadOnly Property _context As ObjectContext
        Get
            Return UnitOfWork.Current
        End Get
    End Property


    Public Sub Add(Of T As {EntityObject, IEntity})(entity As T) Implements IRepository.Add
        _context.CreateObjectSet(Of T)().AddObject(entity)
    End Sub

    Public Sub Attach(Of T As EntityObject)(entity As T) Implements IRepository.Attach
        _context.Attach(entity)
        '_context.CreateObjectSet(Of T)().Attach(entity)
    End Sub

    Public Sub Update(Of T As {EntityObject, IEntity})(ByVal entity As T) Implements IRepository.Update
        _context.CreateObjectSet(Of T)().Attach(entity)
        _context.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified)
    End Sub

    Public Function ExecuteQuery(Of T As {EntityObject, IEntity})(query As IQuery(Of T)) As IQueryResult(Of T) Implements IRepository.ExecuteQuery
        Return query.Execute(_context.CreateObjectSet(Of T))
    End Function

    Public Function ExecuteQuery(Of T As {EntityObject, IEntity}, TResult)(ByVal query As IQuery(Of T, TResult)) As IQueryResult(Of TResult) Implements IRepository.ExecuteQuery
        Return query.Execute(_context.CreateObjectSet(Of T))
    End Function

    Public Function GetAll(Of T As {EntityObject, IEntity})() As IEnumerable(Of T) Implements IRepository.GetAll
        Return _context.CreateObjectSet(Of T).ToList().Where(Function(c) c.EntityState <> EntityState.Deleted)
    End Function

    Public Function GetEntity(Of T As {EntityObject, IEntity})(id As Integer) As T Implements IRepository.GetEntity
        Dim entity = _context.CreateObjectSet(Of T)().SingleOrDefault(Function(x) x.Id = id)
        Return ReturnEntityWhenItExists(entity)
    End Function

    Public Sub Remove(Of T As EntityObject)(entity As T) Implements IRepository.Remove
        _context.DeleteObject(entity)
    End Sub

    Private Function ReturnEntityWhenItExists(Of T As {EntityObject, IEntity})(ByVal entity As T) As T
        If entity IsNot Nothing AndAlso entity.EntityState <> EntityState.Deleted Then
            Return entity
        End If
        Throw New EntityNotFoundException(Of T)()
    End Function
End Class

The Unit of working wrapping this all:

Public Class UnitOfWork
    Implements IDisposable

    Private Shared _dataContext As ObjectContext

    Public Shared ReadOnly Property Current As ObjectContext
        Get
            If _dataContext Is Nothing Then
                Throw New UnitOfWorkException(ResourceRetriever.GetResource(Of String)("ExceptionUnitOfWorkNotInitializedMessage"))
            End If
            Return _dataContext
        End Get
    End Property

    Public Sub New()
        Me.New(New YourDataModelContainer())
    End Sub

    Public Sub New(ByVal objectContext As ObjectContext)
        If _dataContext IsNot Nothing Then
            Throw New UnitOfWorkException(ResourceRetriever.GetResource(Of String)("ExceptionUnitOfWorkAlreadyInitializedMessage"))
        End If
        _dataContext = objectContext
    End Sub

    Public Sub Complete()
        If _dataContext Is Nothing Then
            Throw New UnitOfWorkException(ResourceRetriever.GetResource(Of String)("ExceptionUnitOfWorkNotInitializedMessage"))
        End If
        _dataContext.SaveChanges()
    End Sub

    Public Sub Dispose() Implements IDisposable.Dispose
        If _dataContext IsNot Nothing Then
            _dataContext.Dispose()
            _dataContext = Nothing
        End If
    End Sub
End Class

An example of a query:

Public Class GetAllBuildingsQuery
Implements IQuery(Of DataAccess.Building, BuildingDto)

Public Function Execute(ByVal objectSet As ObjectSet(Of Building)) As IQueryResult(Of BuildingDto) Implements IQuery(Of Building, BuildingDto).Execute
    Dim query = objectSet.Select(Function(g) New BuildingDto() With {.Description = g.Name, .Id = g.Id})
    Return New LinqResult(Of BuildingDto)(query)
End Function
End Class

How it is implemented in the service call:

Public Class EmployeeService
    Implements IEmployeeService


    Private ReadOnly _repository As IRepository
    Public Sub New(ByVal repository As IRepository)
        _repository = repository
    End Sub

    Public Function GetAllEmployees() As IEnumerable(Of EmployeeOverviewDto) Implements IEmployeeService.GetAllEmployees
        Dim employees = _repository.ExecuteQuery(EmployeeQueries.GetAllEmployeesForOverview()).List()
        Return employees
    End Function
End Class
JMan
  • 2,611
  • 3
  • 30
  • 51
  • The question specifically says C#, and this is VB.NET... I know they're related, but still. – anaximander Mar 21 '14 at 08:46
  • @anaximander :I know the question is c#, but willing to share my code. Juts not going to put in the effort to translate this into c#. I'll remove my answer if it's not ok. http://converter.telerik.com/ is always a helpfull tool – JMan Mar 21 '14 at 08:51