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

AddDbContext: DbContextOptions should have the same ServiceLifetime as TContext #6863

Closed
jussik opened this issue Oct 26, 2016 · 6 comments
Closed

Comments

@jussik
Copy link

jussik commented Oct 26, 2016

Steps to reproduce

Make an ASP.NET Core application with a scoped service that provides a connection string to Entity Framework Core per-request.

services.AddDbContext<MyDbContext>((srv, opts) =>
{
    string cs = srv.GetRequiredService<SomeScopedService>().ConnectionString;
    if (string.IsNullOrEmpty(cs))
        throw new NotSupportedException("No ConnectionString");
    opts.UseSqlServer(cs);
});

The issue

This does not work as expected using the default AddDbContext, it does not return the correct instance of the requested scoped service. If I make my own identical AddDbContext except for registering DbContextOptions and DbContextOptions<TContext> as scoped then it works as expected; the correct instance of the scoped service is accessed and I get my connection string.

Proposal

I propose using the contextLifetime parameter of AddDbContext<TContext> to register DbContextOptions and DbContextOptions<TContext> using the same ServiceLifetime as TContext.

Further technical details

EF Core version: 1.0.1
ASP.NET Core version: 1.0.1
Operating system: Windows 7
Visual Studio version: VS 2015 Update 3

@rowanmiller rowanmiller added this to the 1.2.0 milestone Oct 31, 2016
@ajcvickers
Copy link
Contributor

@jussik It is intentional that DbContextOptions is registered as a singleton so that the delegate is only executed once and the resultant options are then re-used. While it is not unreasonable to want to instead have the delegate executed every time, we discussed in triage and decided that we're not going to change the behavior since the existing behavior has perf benefits and seems a good default.

There are various things you can do to get the behavior that you want. As you suggested, you can write your own code to add DbContextOptions as scoped--AddDbContext is only sugar for a common pattern and it is always okay to do something custom instead. Alternatively, you could have your DbContext class depend on your scoped service in its constructor and then use OnConfiguring to set the connection string with info from that scoped service.

@JeanCollas
Copy link

JeanCollas commented Feb 28, 2018

@ajcvickers How would you explain this?
I have some DbContext registered. To avoid any scope issue, I tried to use DbContextOptions in a filter, but get an error:

DbContext registration

            services.AddDbContext<TranslationDbContext>(options =>
                    options.UseSqlServer(Configuration.GetConnectionString("TranslateConnection")));
            services.AddTransient<ITranslationDbContext, TranslationDbContext>();
            services.AddSingleton<TranslationService, TranslationService>();

I get this error:

Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions`1[TranslationDbContext]' from singleton 'TranslationService'.

From what you wrote above, it should not be scoped but singleton? The default has changed?

@ajcvickers
Copy link
Contributor

@JeanCollas Yes, the default was changed. See PR here: #9009

@JeanCollas
Copy link

Ok. Changing the default behavior is strange... It means it breaks apps when upgrading the libs. Why not just allow the option to do that without breaking everything?

@ajcvickers
Copy link
Contributor

@JeanCollas Because on balance the new default was better, the risk of breaking was quite low, and 2.0 was a new major release that intentionally had many breaking changes over 1.1 to improve architecture and experience in several areas.

@caybokotze
Copy link

In my opinion, a good use case for registering a DbContext as a transient dependency is within worker services that are registered as singletons. You can not use scoped dependencies within singleton dependencies. So the only option you have is to register the DbContext as either singleton or transient. Something to bear in mind is that the injected DbContextOptions class lifetime also needs to be updated. You can do both by specifying the service lifetime as follows.

services.AddDbContext<DataContext>(options =>
        {
            options.UseMySQL(configurationRoot.GetConnectionString("DefaultConnection"));
            options.UseLazyLoadingProxies();
        }, ServiceLifetime.Transient, ServiceLifetime.Transient);

The third parameter is for the service lifetime of the DbContextOptions instance.

Hope that helps someone.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 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
Projects
None yet
Development

No branches or pull requests

5 participants