Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C#/EF and the Repository Pattern: Where to put the ObjectContext in a solution with multiple repositories?

I have multiple repositories in my application. Where should I put the ObjectContext? Right now, I have a reference like ObjectContext ctx; in every repository. What is the smartest and safest way to go about this?

like image 962
Olsen Avatar asked Feb 17 '11 18:02

Olsen


1 Answers

A design with multiple ObjectContext instances is only acceptable if your Repository methods commit the transaction. Otherwise, it is possible that external calls to commit the transaction may not persist everything you intend, because you will hold references to different instances of the ObjectContext.

If you want to restrict the ObjectContext to a single instance, then you can build a RepositoryProvider class that contains the ObjectContext, and manages the propagation of repository actions to data commits. This can be best accomplished by either, - Injecting the ObjectContext reference into each repository, or - Subscribing the repositories' events to EventHandlers that call the appropriate methods on the ObjectContext.

The following is a highly pluggable implementation that I have used:

Repository Provider Interface

public interface IRepositoryProvider
{
    IRepository this[Type repositoryType] { get; }
}

Repository Factory Interface

The implementation has dependency on an IEnumerable<IFilteredRepositoryFactory>.

public interface IFilteredRepositoryFactory{
   bool CanCreateRepository(Type repositoryType);
   IRepository CreateRepository(Type repositoryType, ObjectContext context);
}

So, the implementation looks like:

Repository Provider Class

public class RepositoryProvider
{
    public RepositoryProvider(ObjectContext context, IEnumerable<IFilteredRepositoryFactory> repositoryFactories)
    {
        _context = context;
        _repositoryFactories = repositoryFactories;
    }

    private readonly ObjectContext _context;
    private readonly IEnumerable<IFilteredRepositoryFactory> _repositoryFactories;
    private readonly Dictionary<Type, IRepository> _loadedRepositories;

    IRepository this[Type repositoryType]
    {
        get
        {
            if(_loadedRepositories.ContainsKey(repositoryType))
            {
                return _loadedRepositories[repositoryType];
            }
            var repository = GetFactory(repositoryType).CreateRepository(repositoryType, _context);
            _loadedRepositories.Add(repositoryType,repository);
            return repository;
        }
    }

    IFilteredRepositoryFactory GetFactory(Type repositoryType)
    {
        //throws an exception if no repository factory is found
        return _repositoryFactories.First(x => x.CanCreateRepository(repositoryType));
    }
}

It should be noted that a new Repository will be created by the first matching factory implementation. So, if the collection of factories contains multiple factories that can create a Repository for the given repository Type, the first IFilteredRepositoryFactory object in the enumerable will be used and any subsequent factories will be ignored. In addition, if there is no registered factory, and exception will be thrown.

like image 175
smartcaveman Avatar answered Sep 30 '22 07:09

smartcaveman