Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue: AddDbContext() in combination with Scaffolding from Database throws InvalidOperationException #13228

Closed
DiedrikDM opened this issue Sep 6, 2018 · 3 comments
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Milestone

Comments

@DiedrikDM
Copy link

If you're new to EF Core the template created by the scaffolding tool (dotnet-cli) can be a bit confusing when you try to use it in combination with the AddDbContext() method. I'm not sure if it is an actual bug of the AddDbContext method or if it is something that can be clarified in the docs...

Steps to reproduce

Start with a new ASP.NET Core application (I chose the MVC template from Visual Studio - No Authentication).
Use the dotnet-cli to scaffold the code from the database.

dotnet ef dbcontext scaffold "Server=.\sqlexpress;Database=NORTHWND;Trusted_Connection=True;MultipleActiveResultSets=true" Microsoft.EntityFrameworkCore.SqlServer

This created the following NORTHWNDContext class. I did not include the "OnModelCreating" method because it is not relevant to my issue. The "OnConfiguring" method contains a check whether the DbContextOptions object has any extensions ( if(!optionsBuilder.IsConfigured) ).

namespace AddDbContextIssue
{
    public partial class NORTHWNDContext : DbContext
    {
        public NORTHWNDContext()
        {
        }

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

        public virtual DbSet<Categories> Categories { get; set; }
        public virtual DbSet<CustomerCustomerDemo> CustomerCustomerDemo { get; set; }
        public virtual DbSet<CustomerDemographics> CustomerDemographics { get; set; }
        public virtual DbSet<Customers> Customers { get; set; }
        public virtual DbSet<Employees> Employees { get; set; }
        public virtual DbSet<EmployeeTerritories> EmployeeTerritories { get; set; }
        public virtual DbSet<OrderDetails> OrderDetails { get; set; }
        public virtual DbSet<Orders> Orders { get; set; }
        public virtual DbSet<Products> Products { get; set; }
        public virtual DbSet<Region> Region { get; set; }
        public virtual DbSet<Shippers> Shippers { get; set; }
        public virtual DbSet<Suppliers> Suppliers { get; set; }
        public virtual DbSet<Territories> Territories { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. See http://go.microsoft.com/fwlink/?LinkId=723263 for guidance on storing connection strings.
                optionsBuilder.UseSqlServer("Server=.\\sqlexpress;Database=NORTHWND;Trusted_Connection=True;MultipleActiveResultSets=true");
            }
        }

In the Startup file, inside the ConfigureServices method I add the following line to add the NORTHWNDContext to Dependency Injection.

services.AddDbContext<NORTHWNDContext>();

To make sure that I use the NORTHWNDContext somewhere I add the following code to the HomeController (I only included the code that I added).

    private NORTHWNDContext context;
    public HomeController(NORTHWNDContext context)
    {
      this.context = context;
      var products = context.Products.ToList();
    }

When I now try to run the application and go to the homepage, we get the following exception:

System.InvalidOperationException
  HResult=0x80131509
  Message=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<TContext> object in its constructor and passes it to the base constructor for DbContext.
  Source=Microsoft.EntityFrameworkCore
  StackTrace:
   at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
   at Microsoft.EntityFrameworkCore.DbContext.get_Model()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityType()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.get_EntityQueryable()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Collections.Generic.IEnumerable<TEntity>.GetEnumerator()
   at System.Collections.Generic.List`1.AddEnumerable(IEnumerable`1 enumerable)
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at AddDbContextIssue.Controllers.HomeController..ctor(NORTHWNDContext context) in C:\Users\DD\source\repos\AddDbContextIssue\AddDbContextIssue\Controllers\HomeController.cs:line 17
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.<>c__DisplayClass4_0.<CreateActivator>b__0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
   at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.<InvokeInnerFilterAsync>d__13.MoveNext()

Source of this issue

The problem exists because inside the OnConfiguring method, by default it checks for any Extensions added to the DbContextOptions object. If you delete the if-statement from your code, the code will now work. But out of curiosity I looked a bit deeper...
If you use services.AddDbContext(), the optionsAction delegate is a null reference, as expected. This will normally make sure that the DbContextOptions object is empty, however if you look at the following piece of code in the EntityFrameworkServiceCollectionExtensions Line 532, this will add a new CoreOptionsExtension from the DbContextOptionsBuilder to the Extensions Dictionary of the DbContextOptions. In other words, even though there are no DbContextOptions specified through the parameterless AddDbContext method, there is actually an extension added to its collection. This inadvertently makes the if-statement from the generated OnConfiguring method obsolete, since there will always be an extension. The only other thing you can do is use AddScoped(typeof(NORTHWNDContext)).

TL;DR

The AddDbContext method (used without parameters) adds a CoreOptionsExtension object, so if you check "optionsBuilder.IsConfigured" - as default from the scaffolding template - it will always return true because of that object.
I'm not sure if this is an issue with the generated template, with the AddDbContext method or with the documentation...

Further technical details

EF Core version: 2.1.1
Database Provider: Microsoft.EntityFrameworkCore.SqlServer
Operating system: Windows 10
IDE: Visual Studio 2017 15.8.0

@ajcvickers ajcvickers self-assigned this Sep 6, 2018
@ajcvickers ajcvickers added this to the 2.2.0 milestone Sep 7, 2018
@birjitningomba
Copy link

I got the same issue but it turns out that an exception was thrown from the validate() method I use to validate the entities before saving the context. I skip the validation during troubleshooting and with that I am able to perform the database operation.

@ajcvickers ajcvickers modified the milestones: 2.2.0-preview2, 2.2.0 Sep 11, 2018
ajcvickers added a commit that referenced this issue Oct 7, 2018
@ajcvickers ajcvickers added the closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. label Oct 7, 2018
ajcvickers added a commit that referenced this issue Oct 8, 2018
@abhiphirke
Copy link

abhiphirke commented Jun 14, 2019

Hello, I am using EDCore 2.2.4 and facing exactly same issue. The above fix (#13536) states that this issue is also fixed. Looking at this fix, I presume I should not get this error. However, I am still facing it. I followed all steps exactly as stated on this page: https://docs.microsoft.com/en-us/ef/core/get-started/aspnetcore/existing-db

Am I supposed to do something in addition to what is stated on this page? Or is my understanding inadequate? Please help.

@ajcvickers
Copy link
Contributor

@abhiphirke If you're still running into problems, then please open a new issue and include a small project/solution that demonstrates the behavior you are seeing.

@ajcvickers ajcvickers modified the milestones: 2.2.0-preview3, 2.2.0 Nov 11, 2019
@ajcvickers ajcvickers removed their assignment Sep 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-bug
Projects
None yet
Development

No branches or pull requests

4 participants