Search code examples
c#asp.net-coredependency-injectionasp.net-core-mvcasp.net-core-2.1

How do I configure a DbContext with dependency injection with .Net Core 2.1?


Service

public void ConfigureServices(IServiceCollection services)
{
    string cs = Configuration.GetConnectionString("Skillcheck"); 
    services.AddDbContext<TicketsystemContext>(options => options.UseSqlServer(cs));
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}

Context

public TicketsystemContext()
{
}

public TicketsystemContext(DbContextOptions<TicketsystemContext> options)
    : base(options)
{
}

// ... rest of the context

Exception

System.InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application service provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions object in its constructor and passes it to the base constructor for DbContext. at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider() at Microsoft.EntityFrameworkCore.DbContext.get_ChangeTracker() at Microsoft.Extensions.Internal.PropertyHelper.CallNullSafePropertyGetter[TDeclaringType,TValue](Func`2 getter, Object target) at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.BindModelCoreAsync(ModelBindingContext bindingContext) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<g__Bind|0>d.MoveNext()

Exception when I remove the empty constructor from the context

System.InvalidOperationException: Could not create an instance of type 'Skillcheck.Models.TicketsystemContext'. Model bound complex types must not be abstract or value types and must have a parameterless constructor. Alternatively, give the 'c' parameter a non-null default value. at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.CreateModel(ModelBindingContext bindingContext) at Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinder.BindModelCoreAsync(ModelBindingContext bindingContext) at Microsoft.AspNetCore.Mvc.ModelBinding.ParameterBinder.BindModelAsync(ActionContext actionContext, IModelBinder modelBinder, IValueProvider valueProvider, ParameterDescriptor parameter, ModelMetadata metadata, Object value)
at Microsoft.AspNetCore.Mvc.Internal.ControllerBinderDelegateProvider.<>c__DisplayClass0_0.<g__Bind|0>d.MoveNext()

  • cs in the Service isn't null and reads the string from appsettings.json successfully
  • after services.AddDbContext the context is in the ServiceCollection and it doesn't throw
  • using the generic DbContextOptions instead of DbContextOptions < TicketsystemContext > in the context constructor doesn't work
  • explicitly adding a IHttpContextAccessor doesn't work either

I temporarily solved it by overriding OnConfiguring, but I want to understand why it's not working. I'm using .Net Core 2.1 on VS2017.

Solution

The error was that instead of

private TicketsystemContext _c;

public HomeController(TicketsystemContext c)
{
    _c = c;
}

public IActionResult Index()
{
    return View(_c.User.First());
}

I used

public IActionResult Index(TicketsystemContext c)
{
    return View(c.User.First());
}

which works when overriding OnConfiguring, but doesn't when configuring while injecting.


Solution

  • Well, the first error is due to the inclusion of a parameterless constructor. That should not be present. Dependency injection will always choose the constructor with the least dependencies to satisfy, which would be the parameterless, but you need DbContextOptions<TContext> injected.

    The second error indicates that you're including the context as a param in an action method. I'm not sure why you're doing that, but you shouldn't. Your context should be injected into the controller itself and set to an ivar on that, so you actions can utilize the ivar. You may be able to prefix the param with [FromServices] to indicate that the modelbinder should ignore it and it should be injected from the service collection, instead, but method injection is an anti-pattern.