1

I am using generic unity of work for EF6 from here: https://genericunitofworkandrepositories.codeplex.com/

I have an application which uses two databases. I've created additional UnitOfWork interface and class that implements original unit of work interface:

namespace Repository.Pattern.UnitOfWork
{
    public interface ILotteryBackOfficeUnitOfWorkAsync : IUnitOfWorkAsync
    {
    }
}

Second unit of work type for second database initialization:

namespace Repository.Pattern.Ef6
{
    public class LotteryBackOfficeUnitOfWork : UnitOfWork, ILotteryBackOfficeUnitOfWorkAsync
    {
        public LotteryBackOfficeUnitOfWork(IDataContextAsync dataContext)
            : base(dataContext)
        { }
    }
}

In unity I register both unit of works for different data contexts:

public static void RegisterTypes(IUnityContainer container)
{
    // NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
    // container.LoadConfiguration();

    // TODO: Register your types here
    // container.RegisterType<IProductRepository, ProductRepository>();

    var purusLotteryConnectionString = WebConfigurationManager.ConnectionStrings["PurusLotteryContext"].ConnectionString;
    var purusLotteryBackOfficeConnectionString = WebConfigurationManager.ConnectionStrings["PurusLotteryBackOfficeContext"].ConnectionString;

    container.RegisterType<IDataContextAsync, PurusLotteryContext>(new InjectionConstructor(purusLotteryConnectionString));
    container.RegisterType<IUnitOfWorkAsync, UnitOfWork>(new HierarchicalLifetimeManager());

    container.RegisterType<IDataContextAsync, PurusLotteryBackOfficeContext>("LotteryBackOfficeContext", new InjectionConstructor(purusLotteryBackOfficeConnectionString));
    container.RegisterType<ILotteryBackOfficeUnitOfWorkAsync, LotteryBackOfficeUnitOfWork>(new HierarchicalLifetimeManager(),
        new InjectionConstructor(container.Resolve<IDataContextAsync>("LotteryBackOfficeContext")));

    container.RegisterType<IHomeService, HomeService>();
}

It works, but is this correct procedure?

sensei
  • 7,044
  • 10
  • 57
  • 125

2 Answers2

3

One error I see is that you resolve during the registration phase. Not only is this dangerous (as is explained in the documentation of a different DI libary), in your case it causes the PurusLotteryBackOfficeContext to be used as constant and therefore injected as Singleton into the LotteryBackOfficeUnitOfWork. In other words, while this might seem to work during development, this will not work, because a DbContext can't be used as singleton.

Instead, you should use Unity's auto-wiring abilities as much as you can, otherwise a DI library has no advantage (and only disadvantages) over building up object graphs by hand.

Besides this, you are violating the Liskov Substitution Principle (LSP) in your design and this is causing you trouble in your DI configuration. You are violating the LSP because you have two incompatible implementations of the same abstraction. Both PurusLotteryContext and PurusLotteryBackOfficeContext implement IDataContextAsync, but they are incompatible, because they can't be interchanged, because they both work on a completely different database schema. Although they might seem to share the same interface, they don't share the same contract. Just look what happens when you inject a PurusLotteryContext into some class that needs to work with the backoffice. The application will break and this means you are violating the LSP.

Solution is to give them both their own independent abstraction. At first this might seem a weird thing to do, because they both have the same methods. But remember that an interface is much more than a set of method signatures; an interface describes a contract and behavior and since both implementations work on a completely different database schema they have a completely different contract. When you separate this, your code would look like this:

public class PurusLotteryContext : IPurusLotteryDataContextAsync { 
    public PurusLotteryContext(string conString) : base(conString) { }
}

public class LotteryUnitOfWork : ILotteryUnitOfWorkAsync {
    public LotteryUnitOfWork(IPurusLotteryDataContextAsync dc) { }
}

public class PurusLotteryBackOfficeContext : IPurusLotteryBackOfficeDataContextAsync { 
    public PurusLotteryBackOfficeContext(string conString) : base(conString) { }
}

public class LotteryBackOfficeUnitOfWork : ILotteryBackOfficeUnitOfWorkAsync {
    public LotteryBackOfficeUnitOfWork(IPurusLotteryBackOfficeDataContextAsync dc) { }
}

And this allows you to have the following registration:

container.Register<IPurusLotteryDataContextAsync>(new HierarchicalLifetimeManager(),
    new InjectionFactory(c => new PurusLotteryContext(purusLotteryConnectionString)));

container.Register<IPurusLotteryBackOfficeDataContextAsync>(
    new HierarchicalLifetimeManager(),
    new InjectionFactory(c => new PurusLotteryBackOfficeContext(
        purusLotteryBackOfficeConnectionString)));

container.RegisterType<ILotteryUnitOfWorkAsync, LotteryUnitOfWork>(
    new HierarchicalLifetimeManager());
container.RegisterType<ILotteryBackOfficeUnitOfWorkAsync, LotteryBackOfficeUnitOfWork>(
    new HierarchicalLifetimeManager());

Notice a few things about this registration:

  1. The data context implementations are registered as hierarchical, because you typically want the Entity Framework DbContext to have a 'per request' lifestyle.
  2. The data context implementations are registered using a factory, compared to using the container's auto-wiring ability. This is because auto-wiring doesn't help for these classes and a factory delegate will not only make the registration simpler, because also more type-safe (the compiler will help us if we make an error).
Steven
  • 166,672
  • 24
  • 332
  • 435
  • I'm actually followed this implementation https://github.com/raghav-rosberg/UnitOfWorkWithMultipleDBContext/tree/master/Source/UoW_MultipleDBContext that violates LSP. But I can have an idea of what gives your solution in real implementation of the class. Should I duplicate whole Unit of work in the both LotteryBackOfficeUnitOfWork and LotteryUnitOfWork ?? – Jerome2606 Nov 15 '16 at 16:18
1

I know my answer comes late, but I want to point to a different direction here instead @Steve suggestion.

What about the named registration? Using a name to register the same interface with different implementations. Make sense that you have a contract and different implementations, there is nothing wrong on that.

If you want to keep the same interface at the time you register both implementations you can do it with a different name, take a look on the answer below

https://stackoverflow.com/a/18665983

Now, the intent for LSP is that derived types must be completely substitutable for their base types and again, in your case doesn't make to create a new contract with the same functionality and different signatures. I disagree with the @Steve suggestion. Bellow I am attaching a great example of LSP

https://stackoverflow.com/a/584732/819153

Another point of interest is the UnitOfWork.

When you're using EntityFramework and you instantiate your DbContext - you're creating a new UnitOfWork.With EntityFramework you can "flush and reset" the UnitofWork by using SaveChanges(), you don't need to SaveChanges just to return the new ID - EF does it in the scope of the transaction already!

Here is a good article that you can read about it.

http://rob.conery.io/2014/03/04/repositories-and-unitofwork-are-not-a-good-idea/

I would recommend injecting directly the DataContext instead of the IUnitOfWork

Hope this help

Community
  • 1
  • 1
Zinov
  • 3,817
  • 5
  • 36
  • 70