The unity container registers the type AcmeDataContext which inherits from System.Data.Linq.DataContext, with a LifetimeManager using HttpContext.

There is a controller factory which gets the controller instances using the unity container. I set-up all my dependencies on the constructors, like this:

// Initialize a new instance of the EmployeeController class
public EmployeeController(IEmployeeService service)
// Initializes a new instance of the EmployeeService class
public EmployeeService(IEmployeeRepository repository) : IEmployeeService
// Initialize a new instance of the EmployeeRepository class
public EmployeeRepository(AcmeDataContext dataContext) : IEmployeeRepository

Whenever a constructor is needed, the unity container resolves a connection, which is used to resolve a data context, then a repository, then a service, and finally the controller.

The issue is that IEmployeeRepository exposes the SubmitChanges method, since the service classes DO NOT have a DataContext reference.

I have been told that the unit of work should be managed from outside the repositories, so it would seem I ought to remove SubmitChanges from my repositories. Why is that?

If this is true, does this mean that I have to declare an IUnitOfWork interface and make every service class dependent upon it? How else can I allow my service classes to manage the unit of work?

Creating a factory is important, because the DataContext implement IDisposable so you want to have ownership over the instance. While some frameworks allow you to dispose objects when not needed anymore, factories make this very explicit.

There are some interesting things to note about this abstract class. The Unit of Work controls and creates the Repositories. A repository is basically something that implements IQueryable<T>. The repository implements properties that return a specific repository. This prevents users from calling uow.GetRepository<Employee>() and this creates a model that is very close to what you are already doing with LINQ to SQL or Entity Framework.

The unit of work implements Insert and Delete operations. In LINQ to SQL these operations are placed on the Table<T> classes, but when you try to implement it this way it will prevent you from abstracting LINQ to SQL away.

Note that you could even remove the IEmployeeService interface and let the controller use the EmployeeService directly. You don't need this interface for unit testing, because you can replace the unit of work during testing preventing the EmployeeService from accessing the database. This will probably also save you a lot of DI configuration, because most DI frameworks know how to instantiate a concrete class.

Step 5: Implement an InMemoryAcmeUnitOfWork for unit testing.

All these abstractions are there for a reason. Unit testing. Now let's create a AcmeUnitOfWork for unit testing purposes:

Repositories are expected to have convenient methods such as GetById or GetByLastName. Of course IQueryable<T> is a generic interface and does not contains such methods. We could clutter our code with calls like context.Employees.Single(e => e.Id == employeeId), but that's really ugly. The perfect solution to this problem is: extension methods:

With these extension methods in place, it allows you to call those GetById and other methods from your code:

var employee = context.Employees.GetById(employeeId);

What the nicest thing is about this code (I use it in production) is that -once in place- it saves you from writing a lot of code for unit testing. You will find yourself adding methods to the AcmeRepositoryExtensions class and properties to the AcmeUnitOfWork class when new entities are added to the system, but you don't need to create new repository classes for production or testing.

This model has of course some shortcomes. The most important perhaps is that LINQ to SQL isn't abstract away completely, because you still use the LINQ to SQL generated entities. Those entity contain EntitySet<T> properties which are specific to LINQ to SQL. I haven't found them to be in the way of proper unit testing, so for me it's not a problem. If you want you can always use POCO objects with LINQ to SQL.

Another shortcome is that complicated LINQ queries can succeed in test but fail in production, because of limitations (or bugs) in the query provider (especially the EF 3.5 query provider sucks). When you do not use this model, you are probably writing custom repository classes that are completely replaced by unit test versions and you will still have the problem of not being able to test queries to your database in unit tests. For this you will need integration tests, wrapped by a transaction.

A last shortcome of this design is the use of Insert and Delete methods on the Unit of Work. While moving them to the repository would force you to have a design with an specific class IRepository<T> : IQueryable<T> interface, it prevents you from other errors. In the solution I use myself I also have InsertAll(IEnumerable) and DeleteAll(IEnumerable) methods. It is however easy to mistype this and write something like context.Delete(context.Messages) (note the use of Delete instead of DeleteAll). This would compile fine, because Delete accepts an object. A design with delete operations on the repository would prevent such statement from compiling, because the repositories are typed.

Perhaps I should have made this into a blog post :-)
–
StevenNov 9 '10 at 20:19

You should - this was a very helpful answer. Now I'm trying to wrap my head around what Szymon says: "In a mature DDD solution however, there should not be need for both UoW and a repository"
–
Ed INov 9 '10 at 22:19

If combining unit of work and repository patterns, some people advocate that UoW should be managed outside of repository so that you could create two repositories (say, CustomerRepository and OrderRepository) and pass them the same UoW instance ensuring that all the changes to the DB will be done atomically when you finally call UoW.Complete().

In a mature DDD solution however, there should not be need for both UoW and a repository. This is because is such a solution aggregate boundaries are defined is such a way, that there is no need of atomic changes involving more than one repository.