We had a habit of using a gitignored
appsettings.Local.json
to override appsettings.Development.json
for anything we didn’t want in version control. Our settings files are fairly large and creating an override for everything just makes it messy to manage both, so that sometimes meant switching ASPNETCORE_ENVIRONMENT
to Local
to ignore other environment settings.
When switching ASPNETCORE_ENVIRONMENT
back to Development
, we would see the error:
System.InvalidOperationException: Cannot resolve scoped service 'ISomeService' from root provider.
The culprit was a line in our startup, where we were resolving the DbContext and using it to migrate our database.
ServiceProvider.GetService<SomeDbContext>().Database.Migrate();
The service provider was at the application level, specifically the one used to manage its lifecycle. This meant that the DbContext would live throughout the whole lifecycle of the application, even though it is Scoped
by default. Luckily, we did not encounter any memory leaks since the DbContext was only used to migrate (and most startups were already fully migrated).
The correct way to do this is to either not run startup migrations at all and leave that to a separate deployment process (I bet most people do this already). Or if you need a scoped service at startup, use a temporary service scope from IServiceScopeFactory.CreateScope()
so that it is disposed after startup ends.
You can avoid these errors in the future by making sure ServiceProviderOptions.ValidateScopes
is set to true. Note that it seems in .NET 5 the default value in non-Development
environments is false
(link).