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

Scaffold-DbContext template not compatible with DbContext pooling (AddDbContextPool) #12604

Closed
Tracked by #22948
simeyla opened this issue Jul 8, 2018 · 6 comments · Fixed by #28708
Closed
Tracked by #22948
Labels
area-scaffolding closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Milestone

Comments

@simeyla
Copy link

simeyla commented Jul 8, 2018

Edit: This issue relates specifically to EF Core DbContext pooling and not more general 'connection pooling'.


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.DbContextPool`1..ctor(DbContextOptions options)
   at Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.<>c__5`2.<AddDbContextPool>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.

@divega divega added closed-out-of-scope This is not something that will be fixed/implemented and the issue is closed. and removed closed-out-of-scope This is not something that will be fixed/implemented and the issue is closed. labels Jul 11, 2018
@divega
Copy link
Contributor

divega commented Jul 11, 2018

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 divega added this to the Backlog milestone Jul 11, 2018
@simeyla
Copy link
Author

simeyla commented Jul 11, 2018

@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 .

@roji
Copy link
Member

roji commented Dec 6, 2018

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.

@divega divega changed the title Scaffold-DbContext template not compatible with connection pooling (AddDbContextPool) Scaffold-DbContext template not compatible with DbContext pooling (AddDbContextPool) Dec 6, 2018
@Michael-Ziluck
Copy link

Michael-Ziluck commented Feb 22, 2019

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.

@skalavala
Copy link

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!

@JohnYoungers
Copy link

Using aspnetcore 5.0, in switching to AddDbContextFactory opposed to AddDbContext I ran into a similar problem (over a year later)

Similar to what @skalavala has done, this is my final scaffolding script (removes the entire constructor instead of replacing it):

dotnet tool update --global dotnet-ef ....
dotnet ef dbcontext scaffold ....

# Remove default constructor
(Get-Content "./MyDir/MyContext.cs" -Raw) -replace "(?ms)MyContext\(\).*?public ", "" | Set-Content "./MyDir/MyContext.cs"

@ajcvickers ajcvickers added this to the 7.0.0-rc1 milestone Aug 15, 2022
@ajcvickers ajcvickers modified the milestones: 7.0.0-rc1, 7.0.0 Nov 5, 2022
@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
area-scaffolding closed-fixed The issue has been fixed and is/will be included in the release indicated by the issue milestone. customer-reported type-enhancement
Projects
None yet
8 participants