GDiff94
GDiff94

Reputation: 147

How to inject DbContext into repository constructor in ASP.NET core

I am writing my first ASP.NET Core Web API and am trying to work out how to inject my DbContext object into my repository constructor. I followed the EF part of this tutorial where the DbContext is registered to the service collection via services.AddDbContext<DbContext> and services.GetRequiredService<DbContext>() is used to initialize the database values.

    public class Startup
    {

        public IConfiguration Configuration { 

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
            services.AddSingleton<IItemRepository, ItemRepository>();
            services.AddSingleton<IUserRepository, UserRepository>();

            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new Info { Title = "My API", Version = "v1" });
            });

            services.AddDbContext<RAFYCContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            //CreateWebHostBuilder(args).Build().Run();

            IWebHost host = CreateWebHostBuilder(args).Build();

            using (IServiceScope scope = host.Services.CreateScope())
            {
                IServiceProvider services = scope.ServiceProvider;
                try
                {
                    RAFYCContext context = services.GetRequiredService<RAFYCContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    ILogger logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred while seeding the database.");
                }
            }

            host.Run();
        }
    }

I am able to inject the DbContext into the Controller and then call a repository method to assign it to the UserRepository:

    public class UserController : ControllerBase
    {
        IUserRepository _userRepo;

        public UserController(IUserRepository userRepo, RAFYCContext context)
        {
            _userRepo = userRepo;
            _userRepo.SetDbContext(context);
        }
    }
    public class UserRepository : IUserRepository
    {
        public RAFYCContext _context;

        public UserRepository() { }

        public void SetDbContext(RAFYCContext context)
        {
            this._context = context;
        }
    }

This works, however I would like to inject the DbContext into the constructor of my repository rather than assigning it in the Controller constructor after instantiation, like below:

    [Route("api/[controller]")]
    [ApiController]
    public class UserController : ControllerBase
    {
        IUserRepository _userRepo;

        public UserController(IUserRepository userRepo)
        {
            _userRepo = userRepo;
        }
    }
    public class UserRepository : IUserRepository
    {
        RAFYCContext _context;

        public UserRepository(RAFYCContext context)
        {
            _context = context;
        }
    }

With this code I get the following error:

InvalidOperationException: Cannot consume scoped service 'RAFYC.MobileAppService.Models.RAFYCContext' from singleton 'RAFYC.MobileAppService.Repositories.IUserRepository'

Does anyone know if this is possible with ASP.NET Core (2.2)?

Upvotes: 5

Views: 12542

Answers (1)

Nkosi
Nkosi

Reputation: 246998

AddDbContext adds a DbContext as scoped by default, which would cause problems with the singleton repositories.

It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.

Reference Dependency injection in ASP.NET Core: Service lifetimes

I would suggest adding the repositories as scoped as well if possible.

services.AddScoped<IItemRepository, ItemRepository>();
services.AddScoped<IUserRepository, UserRepository>();

Upvotes: 6

Related Questions