UOW + Repository + Autofac load two different DbContext

柔情痞子 提交于 2019-12-06 07:00:02

问题


I'm facing an issue today and I'm unable to solve it, I searched a lot and can't get into a solution, please help me if you can.

I'm implementing a MVC application which uses EF + Repository Pattern + Unit Of Work with Autofac as the Dependency Injector.

I was able to work with one DbContext class, but I'm facing a situation where I need to use another DbContext instance (which access another database with another user credentials)

Let me explain better: I have EntityA which comes from database A (and have a DatabaseA_Context class). So I need a EntityB, which comes from database B (with its own DatabaseB_Context class).

When I register they with AutoFac, only the last configured dependency is injected on the GenericRepository implementation.

I've already found articles saying that Autofac overrides the registration with the last value.

I've already found other article that shows if I pass an IEnumerable on the UnitOfWork constructor, I'm able to see all of the registered types for it, but I want a specific one.

Am I clear enough?

My code below:

My controller:

public class MyController : Controller
{
    private readonly IBaseBLL<EntityA> _aBLL;
    private readonly IBaseBLL<EntityB> _bBll;

    public MyController(IBaseBLL<EntityA> aBLL, IBaseBLL<EntityB> bBLL)
    {
        _aBLL = aBLL;
        _bBLL = bBLL;
    }
}

My Business Layer

public interface IBaseBLL<T> where T : class
{
    T Select(Expression<Func<T, bool>> predicate);
    T AddT entity);
    void Update(T entity);
    T Delete(T entity);
}

public class BaseBLL<T> : IBaseBLL<T> where T : class
{
    private readonly IUnitOfWork _uow;

    public BaseBLL(IUnitOfWork uow)
    {
        _uow = uow;
    }

    //implementation goes here...
}

My UOW implementation

public interface IUnitOfWork : IDisposable
{
    int SaveChanges();
    IGenericRepository<T> Repository<T>() where T : class;
}

public class UnitOfWork : IUnitOfWork
{
    private readonly DbContext _dbContext;
    private bool disposed = false;
    private Dictionary<Type, object> repositories;


    public UnitOfWork(DbContext dbContext)
    {
        _dbContext = dbContext;
        repositories = new Dictionary<Type, object>();
    }

    public IGenericReposity<T> Repository<T>() where T : class
    {
        if (repositories.Keys.Contains(typeof(T)))
            return repositories[typeof(T)] as IGenericReposity<T>;

        IGenericReposity<T> repository = new GenericRepository<T>(_dbContext);
        repositories.Add(typeof(T), repository );
        return repository ;
    }

    public int SaveChanges()
    {
        return _dbContext.SaveChanges();
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
            if (disposing)
                _dbContext.Dispose();

        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

My Repository implementation

public class GenericRepository<T> : IGenericRepositoryT> where T : class
{
    protected readonly DbContext _dbContext;
    protected IDbSet<T> _dbSet;

    public GenericRepository(DbContext dbContext)
    {
        _dbContext = dbContext;
        _dbSet = _dbContext.Set<T>();
    }

    //implementation goes here...
}

My AutoFac registration (in Global.asax file)

var builder = new ContainerBuilder();

builder.RegisterType(typeof(DatabaseA_Context)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType(typeof(DatabaseB_Context)).As(typeof(DbContext)).InstancePerLifetimeScope();
builder.RegisterType(typeof(UnitOfWork)).As(typeof(IUnitOfWork)).InstancePerRequest(); 

Please help


回答1:


You should use Named and Keyed Service

builder.RegisterType<DatabaseA_Context>()
       .Named<DbContext>("databaseA")
       .InstancePerLifetimeScope();
builder.RegisterType<DatabaseB_Context>()
       .Named<DbContext>("databaseB")
       .InstancePerLifetimeScope();

Then you can specify the DbContext you want for a component at registration

builder.RegisterType<MyService>()
       .As<IService>()
       .WithParameter((pi, c) => pi.Name == "dbContext", 
                      (pi, c) => c.ResolveNamed<DbContext>("databaseA"))

or by using a IIndex<,>

public class MyService : IService
{
    public MyService(IIndex<String, DbContext> dbContexts)
    {
        var databaseA = dbContexts["databaseA"];
    }
}

Autofac also support specifying named registration with the WithKeyAttribute

public class MyService : IService
{
    public MyService([WithKey("DatabaseA")DbContext dbContext)
    {
    }
}

See the metadata documentation for more info on how to get the WithKeyAttribute set up.

With this solution, DbContext won't be registered. If you want a default DbContext you can register one like this :

builder.Register(c => c.ResolveNamed<DbContext>("databaseA"))
       .As<DbContext>()
       .InstancePerLifetimeScope(); 

You can also use a module that will choose the correct registration based on the name of the argument :

public class MyService : IService
{
    public MyService(DbContext dbContextA, DbContext dbContextB)
    {
    }
}

To do this you will need to register this Autofac Module

public class DbContextModule : Module
{
    protected override void AttachToComponentRegistration(
        IComponentRegistry componentRegistry, IComponentRegistration registration)
    {
        registration.Preparing += Registration_Preparing;
    }

    private void Registration_Preparing(Object sender, PreparingEventArgs e)
    {
        Parameter parameter = new ResolvedParameter(
                                (pi, c) => pi.ParameterType == typeof(DbContext),
                                (pi, c) =>
                                {
                                    if (pi.Name.Equals("dbContextA", StringComparison.OrdinalIgnoreCase))
                                    {
                                        return c.ResolveNamed<DbContext>("databaseA");
                                    }
                                    else if (pi.Name.Equals("dbContextB", StringComparison.OrdinalIgnoreCase))
                                    {
                                        return c.ResolveNamed<DbContext>("databaseB");
                                    }
                                    else
                                    {
                                        throw new NotSupportedException($"DbContext not found for '{pi.Name}' parameter name");
                                    }
                                });
        e.Parameters = e.Parameters.Concat(new Parameter[] { parameter });
    }
}

and

builder.RegisterModule<DbContextModule>()


来源:https://stackoverflow.com/questions/33379624/uow-repository-autofac-load-two-different-dbcontext

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!