Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.0k views
in Technique[技术] by (71.8m points)

asp.net core - The DbContext of type cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions

I am trying to upgrade our current .Net Core application from 1.1 to 2.0 and am getting this runtime error: "The DbContext of type 'CoreContext' cannot be pooled because it does not have a single public constructor accepting a single parameter of type DbContextOptions".

It is caused by using the new IServiceCollection.AddDbContextPool<> function. When I use IServiceCollection.AddDbContext<> it still works.

This application is DB-First, so I generate all our contexts using 'Scaffold-DbContext'. Due to that, and the need to inject other services I have an extension on every context like this:

public partial class CoreContext
{
    public CoreContext(
        DbContextOptions<CoreContext> options,
        IUserService userService,
        IAuditRepository auditRepository
        ) : base(options) {...}
}

Whenever I run the Scaffold-DbContext I just remove the autogenerated Constructor from CoreContext, but even if I put it in there I still get this error.

public partial class CoreContext : DbContext
{
    public CoreContext(DbContextOptions<CoreContext> options) : base(options) {}
}

I've already updated Program.cs to the new style:

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

    public static IWebHost BuildWebHost(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
                .UseKestrel()
                .UseContentRoot(Directory.GetCurrentDirectory())
                .UseIISIntegration()
                .UseStartup<Startup>()
                .Build();
}

And the Startup.cs is pretty straightforward:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    ...
    services.AddDbContextPool<CoreContext>(options => options.UseSqlServer(absConnectionString));
    ...
}

I am using Autofac for DI if that helps. For now I'll default back to the non-Pooling alternative, but it would be nice to take advantage of this feature.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

When using DbContext Pooling, your own state (e.g. private fields) in your derived DbContext class will be preserved. Which means the lifetime of your services is now singleton. That's why you shouldn't have other injected services here. But it's possible to query the required services this way: First we should use the UseInternalServiceProvider method on DbContextOptionsBuilder to tell EF which service provider to use for its services. This service provider must have all the services configured for EF and any providers. So we should register EF Services manually:

services.AddEntityFrameworkSqlServer();

And then introduce the application's services provider which now includes the EF Services too:

services.AddDbContextPool<ApplicationDbContext>((serviceProvider, optionsBuilder) =>
{
   optionsBuilder.UseSqlServer("...");
   optionsBuilder.UseInternalServiceProvider(serviceProvider);
});

After that define these namespaces:

using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.Extensions.DependencyInjection;

And now you can access the registered services in the application within the ApplicationDbContext class using the following methods

var siteSettings = this.GetService<IOptionsSnapshot<SiteSettings>>();

Or

var siteSettings = this.GetInfrastructure().GetRequiredService<IOptionsSnapshot<SiteSettings>>();

this is the current instance of the DbContext.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...