简体   繁体   中英

How to inject dynamic DbContext object into repository using Autofac

I have an .net core web api application where I'm using entity framework core with service layer, unit of work and repository layer pattern. For DI I'm using Autofac.

The application has multiple clients and each client has its own database and the schema for all these databases is same. With each API call I'll get the client specific connection string, using which I have to create a DbContext and use it for all its operations.

On Startup class I have registered my dbcontext ClientDbContext and all other classes. When the unit-of-work class is called I am creating my new DbContext based on the connection string. I want the repository to use this instance, but the repository is still using the initial ClientDbContext instance which was created at startup .

How can I make the repository use the new DbContext instance?

Unit of Work:

public class UnitOfWork : IUnitOfWork
{
    public ClientDbContext ClientDbContext { get; private set; }        

    public UnitOfWork ()
    {            

    }

    public void SetDbContext(string connectionString)
    {
        if(ClientDbContext == null)
        {
            //creating new db context instance here
            ClientDbContext = MembershipRepository.CreateDbContext(connectionString);
        }                
    }        
    //property injection
    public IGenericRepository<SomeEntity, ClientDbContext> SomeEntityGenericRepository { get; }
}

Generic Repository:

public class GenericRepository<TEntity, TDbContext> : IGenericRepository<TEntity, TDbContext> where TEntity : class
where TDbContext : DbContext
{
    private readonly TDbContext _context;
    private readonly DbSet<TEntity> _dbset;
    public GenericRepository(TDbContext context)
    {
        // need to get updated context here, but getting the initial one
        _context = context;
        _dbset = _context.Set<TEntity>();
    }
}

Autofac module called in Startup.cs:

builder.Register(a => new ClientDbContext()).InstancePerLifetimeScope();

builder.RegisterGeneric(typeof(GenericRepository<,>)).As(typeof(IGenericRepository<,>)).InstancePerLifetimeScope();

//Register Unit of Work here
builder.RegisterType<UnitOfWork>().As<IUnitOfWork>().InstancePerLifetimeScope().PropertiesAutowired();                

//Register Services here
builder.RegisterType<SomeService>().As<ISomeService>().InstancePerLifetimeScope();

Can anyone please help me out on how to achieve the above requirement?

Is there any way I can make Autofac use my new created dbcontext object?

Instead of

builder.Register(a => new ClientDbContext()).InstancePerLifetimeScope();

you could use

builder.Register(c => c.Resolve<IUnitOfWork>().ClientDbContext)
       .InstancePerLifetimeScope(); 

By the way I'm not sure what is the responsibility of your IUnitOfWork . Another way of doing this would be to have a class that would provide information about the current user :

public interface IClientContext 
{
    public String ClientIdentifier { get; }
}

Then a DbContextFactory that would create the DbContext based on the IClientContext

public interface IDbContextFactory 
{
    IDbContext CreateDbContext(); 
}

public class DbContextFactory 
{
    public DbContextFactory(IClientContext clientContext) 
    {
        this._clientContext = clientContext; 
    }

    private readonly IClientContext _clientContext;

    public IDbContext CreateDbContext()
    {
        // get the connectionstring from IClientContext and return the IDbContext
    } 
}

The concrete implementation of IClientContext depends on the way you can get this information, it could be from current HttpContext or any other way it's up to you. It seems that at some point you call SetDbContext you can keep this way by creating a XXXClientContextProvider where XXX is relative to the way you get this information.

public class XXXClientContextProvider 
{
    private IClientContext _clientContext; 

    public IClientContext GetClientContext()
    {
        if(this._clientContext == null) 
        {
           throw new Exception("client context is null. You should do X or Y"); 
        }
        return this._clientContext; 
    }
    public void SetClientContext(String clientId) 
    {
        if(this._clientContext != null) 
        {
            throw new Exception("client context has already been set"); 
        }
        this._clientContext = new StaticClientContext(clientId);
    }
}

and then register everything like this :

builder.Register(c => c.Resolve<IClientContextProvider>().GetClientContext())
       .As<IClientContext>()
       .InstancePerLifetime(); 

builder.Register(c => c.Resolve<IDbContextFactory>().CreateDbContext())
       .As<IDbContext>()
       .InstancePerLifetime(); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM