Reputation: 2347
I have a generic UnitOfWork pattern implementation and these UnitOfWork objects are dependencies to my service classes. Below snippets should help the reader understand my code setup:
IUnitOfWork interface
public interface IUnitOfWork<out TContext> where TContext : IDbContext
UnitOfWork class
public sealed class UnitOfWork<TContext> : IDisposable, IUnitOfWork<IDbContext> where TContext : IDbContext
{
private static readonly ILog Log = LogManager.GetLogger(typeof(UnitOfWork<TContext>));
private readonly IDbContext _dbContext;
private Dictionary<string, IRepository> _repositories;
private IDbTransaction Transaction { get; set; }
public UnitOfWork(IDbContext context)
{
_dbContext = context;
}
}
Container registrations:
builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>));
builder.RegisterType<ReconciliationDbContext>().As<IDbContext>();
builder.RegisterType<GenevaDataDbContext>().As<IDbContext>();
builder.RegisterType<OpenStaarsDbContext>().As<IDbContext>();
builder.RegisterType<UnitOfWork<ReconciliationDbContext>>().Keyed<IUnitOfWork<IDbContext>>(ContextKey.Recon);
builder.RegisterType<UnitOfWork<OpenStaarsDbContext>>().Keyed<IUnitOfWork<IDbContext>>(ContextKey.OpenStaars);
builder.RegisterType<CommentsService>().As<ICommentsService>().WithAttributeFiltering();
DbContext classes:
public class ReconciliationDbContext : BaseDbContext<ReconciliationDbContext>, IDbContext
{
private const string DbSchema = "BoxedPosition";
public ReconciliationDbContext() : base("Reconciliation")
{
}
}
public class OpenStaarsDbContext : BaseDbContext<OpenStaarsDbContext>, IDbContext
{
public OpenStaarsDbContext() : base("OpenStaars")
{
}
}
CommentsService class:
public class CommentsService : ICommentsService
{
private readonly IUnitOfWork<IDbContext> _reconciliationUoW;
public CommentsService([KeyFilter(ContextKey.Recon)] IUnitOfWork<IDbContext> reconciliationUoW)
{
_reconciliationUoW = reconciliationUoW;
}
}
Resolving ICommentsService:
var commentsService = container.Resolve<ICommentsService>();
Now when I try to resolve the ICommentsService type, it instantiates the UnitOfWork dependency. However, the UnitOfWork._dbContext property evaluates to OpenStaarsDbContext type. This is particularly strange considering our registrations.
It becomes more strange if we re-order our IDbContext registrations by registering the GenevaDataDbContext after OpenStaarsDbContext. Now the _dbContext evaluates to GenevaDataDbContext instance.
How can I fix this to make the reconciliationUoW dependency of CommentsService to have the correct instance of ReconciliationDbContext ?
Upvotes: 1
Views: 1218
Reputation: 3329
The reason of such behaviour is the fact you inject IDbContext
into your UnitOfWork
constructor, instead of TContext
- the container just ignores the type you provide as a generic parameter in you registration and takes the first IDbContext
it finds in the container - which would be the last registered, no matter what key you would use.
To make it work, instead of using keyed registration, you could simply inject IUnitOfWork<ContextYouNeed>
instead of IUnitOfWork<IDbContext>
- it would simplify the code as well. First you need to fix your UnitOfWork
class:
class UnitOfWork<TContext> : IUnitOfWork<TContext> where TContext : IDbContext
{
private readonly TContext _context;
public UnitOfWork(TContext context)
{
_context = context;
}
}
In your registration, you do not need to register specific unit of work types, standard generic registration would be sufficient. But you need to register your context types AsSelf
as well, so Autofac would properly inject it for your unit of work instances:
builder.RegisterGeneric(typeof(UnitOfWork<>)).As(typeof(IUnitOfWork<>));
builder.RegisterType<ReconciliationContext>().As<IContext>().AsSelf();
builder.RegisterType<OpenStaarsContext>().As<IContext>().AsSelf();
Later on, in your service simply inject proper unit of work:
public CommentsService(IUnitOfWork<ReconciliationContext> reconciliationUoW)
Upvotes: 2