Configuring DBContext in the constructor of my base repository class

前端 未结 2 2100
离开以前
离开以前 2020-12-22 08:01

I have a situation where I need to instantiate my DBContext after my solution has started up. I asked this question which indicated that I could do this with a constructor a

2条回答
  •  暗喜
    暗喜 (楼主)
    2020-12-22 08:22

    Edited

    The answer has been edited to rectify the mistake spotted and fixed by Nkosi. Thanks, @Nkosi.

    Implement a factory pattern. You can create a factory, call it ContextFactory as below:

    First, define the interface. Further modified, removed the connectionString parameter

    public interface IContextFactory where T : DbContext
    {
        T CreateDbContext();
    }
    

    Create a factory class that implements this interface (edited as per Nkosi answer). Further modified to inject IHttpContextAccessor

    public class ContextFactory : IContextFactory where T : DbContext
    {
        private readonly HttpContext _httpContext;
    
        public ContextFactory(IHttpContextAccessor contextAccessor)
        {
            _httpContext = contextAccessor.HttpContext;
        }
    
        public T CreateDbContext()
        {
            // retreive the connectionString from the _httpContext.Items
            // this is saved in the controller action method
            var connectionString = (string)_httpContext.Items["connection-string"];
            var optionsBuilder = new DbContextOptionsBuilder();
            optionsBuilder.UseSqlServer(connectionString);
            return (T)Activator.CreateInstance(typeof(T), optionsBuilder.Options);
        }
    }
    

    Then modify your base repository and make the JobsLedgerAPIContext protected. This context is going to be set by the derived class. Further modified to remove the constructor. It will use the parameterless constructor.

    public class EntityBaseRepository : IEntityBaseRepository where T : class, IEntityBase, new()
    {
        protected JobsLedgerApiContext Context { get; set; }
    
        public virtual IQueryable GetAll()
        {
            return Context.Set().AsQueryable();
        }
    
        public virtual int Count()
        {
            return Context.Set().Count();
        }
    }
    

    Change your derived class to use IContextFactory. Further modified to use the _contextFactory.CreateDbContext() parameter less method

    The IClientRepository should have SetContext method defined.

    public class ClientRepository : EntityBaseRepository, IClientRepository
    {
        private readonly IContextFactory _contextFactory;
    
        public ClientRepository(IContextFactory factory)
        {
            _contextFactory = factory;
        }
    
        // this method will set the protected Context property using the context
        // created by the factory
        public void SetContext()
        {
            Context = _contextFactory.CreateDbContext();
        }
    
        public void RelatedSuburbEntities(Suburb suburb)
        {
            Context.Entry(suburb).Reference(a => a.State).Load();
        }
    }
    

    In the controller, that receives IClientRepository instance, you can set the connection in the HttpContext.Items, which will be valid for the request. This value will then be retrieved by the ContextFactory using IHttpContextAccessor. Then you simply call the _repository.SetContext(); method on the repository.

    public class HomeController : Controller
    {
        private readonly IClientRepository _repository;
    
        public HomeController(IClientRepository repository)
        {
            _repository = repository;
        }
    
        public IActionResult Index()
        {
           // save the connectionString in the HttpContext.Items
           HttpContext.Items["connection-string"] = "test-connection";
    
           // set the context 
           _repository.SetContext();
    
           return View();
        }
    }
    

    Make sure you register the IContextFactory in ConfigureServices as open generics and Singleton as below, also register the HttpContextAccessor and IClientRepository

    services.AddHttpContextAccessor();
    services.AddSingleton(typeof(IContextFactory<>), typeof(ContextFactory<>));
    services.AddTransient();
    

提交回复
热议问题