Efcore: Scaffold-DbContext template not compatible with DbContext pooling (AddDbContextPool)

Created on 8 Jul 2018  路  5Comments  路  Source: dotnet/efcore

After updating to EF Core 2.1 I noticed that an additional constructor (which passes through options) is now being included by the template. I previously had to add the DbContextOptions constructor in a partial class to get connection pooling to work.

Here is the current generated code (two constructors) :

```
public partial class RRStoreContext : DbContext
{
public RRStoreContext()
{
}

    public RRStoreContext(DbContextOptions<RRStoreContext> options)
        : base(options)
    {
    }
I declare the context like this using a connection pool:

            services.AddDbContextPool<RRStoreContext>(options =>
            {
                var connection = CONNECTION_STRING;
                options.UseSqlServer(connection, sqlOptions =>
                {
                    sqlOptions.EnableRetryOnFailure();
                });               
            });

When I run it I get this error:

Exception message:
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: The DbContext of type 'RRStoreContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions.
at Microsoft.EntityFrameworkCore.Internal.DbContextPool1..ctor(DbContextOptions options) at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__52.b__5_1(IServiceProvider sp)
at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope)
```

The presence of the additional parameterless constructor is what is causing the problem.

Workaround

Manually comment out the parameterless constructor.

    //public RRStoreContext()
    //{
    //}

Steps to reproduce

Run Scaffold-DbContext, and try to use services.AddDbContextPool

Further technical details

EF Core version: 2.1.1
Database Provider: SqlServer 2.1.1
Operating system: VS 15.8 Preview 3

This did not used to occur because there were no parameters, in fact I like I said I used to have to manually add the constructor.

customer-reported type-enhancement

Most helpful comment

Not sure why this is marked as just an enhancement rather than a serious bug. Right now, there's no way to actually use Entity Framework's code generator without doing a lot of extra work to make it work properly.

My steps were: generate a Database-First model, then I tried to generate a controller using the context and models that were made. That's not a small edge case, that's what most people use Entity Framework for.

All 5 comments

EF Triage: we are optimizing the code generated automatically for simple scenarios. You should expect to have to modify the code in certain cases.

That said, looking at this issue now, it seems reasonable to relax the restriction on multiple constructors for DbContext Pooling. A warning should be enough.

@divega thanks.

It's obviously easy to modify the code to add a constructor, but a much bigger pain to remove it.
Another possibility would be to add a scaffolding option to not produce the default constructor?

BTW. The only reason I enabled connection pooling was because of the massive number of warning messages I'd get in the console just from initializing the context - this was unbearable when a new context was created for every request. I will need to check again with 2.1 to see if the warnings I got are still there .

Came across this by chance, it's worth mentioning that this doesn't seem to be about connection pooling, but rather about DbContext pooling - the two are very different things. Connection pooling is typically enabled regardless of whether DbContexts are pooled or not. It may be worth updating the title accordingly etc.

Not sure why this is marked as just an enhancement rather than a serious bug. Right now, there's no way to actually use Entity Framework's code generator without doing a lot of extra work to make it work properly.

My steps were: generate a Database-First model, then I tried to generate a controller using the context and models that were made. That's not a small edge case, that's what most people use Entity Framework for.

I agree with the comments above - at least there should be a command line option that should let you generate the code the way you want it...

Use Visual Studio's Pre-Build events, and write a small powershell script that renames the default DbContext constructor to something else. This is not the ideal solution, but a hack. This saved me time, and I don't have to manually remove the constructor every time I run scaffold script. Steps to do this:

  1. Right click on Project, and select Properties
  2. Go to Build Events Tab
  3. Enter the following command in the textbox right under Pre-build event command line:. (make sure you change the filename).
  4. Save and run Build.
  5. The default constructor should be replaced with a useless method, called foo(). You could remove the constructor entirely, you just have to figure out a way to consider newline characters, and pattern matching...etc.
powershell.exe -ExecutionPolicy Bypass -NoProfile -NonInteractive -Command "(gc $(ProjectDir)\Entity\YourDBContext.cs) -replace 'public YourDbContext\(\)', 'public void Foo()' | Out-File -encoding ASCII $(ProjectDir)\Entity\YourDbContext.cs"

Basically what this script does is, it runs the powershell in a non-interactive mode with no profile (for faster loading), and replaces the constructor code in the DbContext.cs file with a method called Foo. saves the file (overwrites) DbContext.cs file.

image

Hopefully this helps someone!

Was this page helpful?
0 / 5 - 0 ratings