I have made an implementation of IHostedService where I will load a cache when my application is starting up. In that implementation of IHostedService I need the Context for the DB, but it is null when I try to get it.
I am loading my service in StartUp ConfigureServices in the last line:
services.AddDbContext<MyContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DatabaseConnection")));
...
services.AddHostedService<InitializeCacheService>();
And my service look like this (I am getting the cache but not the context):
public class InitializeCacheService : IHostedService
{
private readonly IServiceScopeFactory _scopeFactory;
public InitializeCacheService(IServiceScopeFactory scopeFactory)
{
this._scopeFactory = scopeFactory;
}
public Task StartAsync(CancellationToken cancellationToken)
{
using (var scope = _scopeFactory.CreateScope())
{
var cache = scope.ServiceProvider.GetService<IMemoryCache>();
var context = scope.ServiceProvider.GetRequiredService<MyContext>();
CacheLogic cacheLogic = new CacheLogic(cache, context);
cacheLogic.LoadProfileCache();
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
return Task.CompletedTask;
}
}
What am I doing wrong?
Thanks very much in advance :-)
UPDATE
This is actually working, the problem is that I missed the initialization of the context in CacheLogic :-| Sorry about that!
But now it is working then i get the error below when I query in CacheLogic, an that might be because it is async and I handle over the Context in a time limited scope in the InitializeCacheService?
But After rethinking my solution I think that the CacheLogic shall be a singleton, and then my problem is how to get access to cache and DBContext in a singleton (I will create a new question for that).
System.ObjectDisposedException: 'Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
ObjectDisposed_ObjectName_Name'
Related
In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}
I have a razor pages with some classes such as scheduled tasks that run in the background. I have a IUnitofWork for the databases and uses EF.
In my schedule class "WorkerService : BackgroundService" it does routine backups and other tasks.
How can I reference the Database because I dont have DI due to not implementing razor pages?
Usually this is how I do it using DI on razor code files:
private readonly IUnitOfWork _unitOfWork;
public IndexModel(IUnitOfWork unitOfWork)
{
_unitOfWork = unitOfWork;
}
I am new to this DI and been in the udemy and microsoft site daily. I think I have to create a IUnit of work and pass in the ApplicationDbContext maybe in an ovveride? But how to get the context without DI.
Program.cs
builder.Services.AddHostedService<WorkerService>(); //Uses cronos to execute DoWork() every hour
WorkerService.cs
private const string schedule = "*/5 * * * *"; // every 5 for testing
private readonly CronExpression _cron;
public WorkerService()
{
_cron = CronExpression.Parse(schedule);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var utcNow = DateTime.UtcNow;
var nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stoppingToken);
await DoBackupAsync();
}
}
private static Task DoBackupAsync()
{
DoWork d = new DoWork();
return Task.FromResult("Done");
}
RazorApp/Pages
This is where I need to save data
RazorApp/ScheduledTasks/DoWork.cs
RazorApp/ScheduledTasks/WorkerService.cs
Attempting to DI either the IUnitOfWork or ApplicationDbContext
Further trying different examples like: https://dotnetcorecentral.com/blog/background-tasks
Results in this error as well: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: Microsoft.Extensions.Hosting.IHostedService Lifetime: Singleton ImplementationType: WebRazor.ScheduledTasks.BackgroundPrinter': Cannot consume scoped service 'WebRazor.DataAccess.ApplicationDbContext' from singleton 'Microsoft.Extensions.Hosting.IHostedService
public BackgroundPrinter(ILogger<BackgroundPrinter> logger, IWorker worker, ApplicationDbContext dbContext)
{
this.logger = logger;
applicationDbContext = dbContext;
}
Is this where I need to get it from the settings directly or is there a slick way to grab the Db Context?
Ok, wow I was all over the place. I implemented this and it worked.
public BackgroundPrinter(ILogger<BackgroundPrinter> logger, IWorker worker, IServiceProvider serviceProvider)
{
this.logger = logger;
unitOfWork = serviceProvider.CreateScope().ServiceProvider.GetRequiredService<IUnitOfWork>();
}
On construct pass in the Iserviceprovider and access this way. DI to the DoWork and its writing to the database.
Still not sure this is the best way to solve this problem, but I think I have to refactor my unitofwork because every now and then I get an error on writing
"A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext" but this may be easy to solve.
The background tasks with hosted services doc describes how to consume a scoped service in a background task:
Inject IServiceProvider in hosted service's ctor
Use the IServiceProvider to create scope during the execution
Use the scope to resolve required services
Personally for "timed" services like provided WorkerService in most cases I found useful to create scope per every iteration (especially if it uses EF internally). Something along this lines:
private readonly IServiceProvider _serviceProvider;
public WorkerService(IServiceProvider serviceProvider)
{
...
_serviceProvider = serviceProvider;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var utcNow = DateTime.UtcNow;
var nextUtc = _cron.GetNextOccurrence(utcNow);
await Task.Delay(nextUtc.Value - utcNow, stoppingToken);
await DoBackupAsync(stoppingToken);
}
}
private static async Task DoBackupAsync(CancellationToken stoppingToken)
{
using (var scope = _serviceProvider.CreateScope()) // do not forget to dispose the scope
{
var backupService = scope.ServiceProvider.GetRequiredService<IBackupService>();
await backupService.BackUp(stoppingToken);
}
}
Let's say I have my own class QueueListener<TService, TPayload> inherited from BackgroundService. It opens TCP connection and listens incoming messages. On each message I would like to initialize service of TService type and pass deserialized from JSON instance of TPayload to it. TService is going to be registered as Transient, so it means to be lightweight and stateless as a handler for payload have to be (in my current task). For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives. Does it sounds like a plan or am I overengineering? I want to avoid TService is singleton as well.
Documentation says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But I am not completely sure what does it means. There is no way to inject scoped service in BackgroundService because it has Singleton lifetime. Do they warn me to stop doing things like I do?
UPD #1
I explain why I suppose to create scope on each message. The idea behind that is to prevent listener to be blocked by message processing and to provide other developers possibility to create their own handlers and do some stuff on received message. Other developers can create database connections for instance while processing and I want it to be closed and released when handling is done.
Register TService as scoped and create a new scope per message. Then resolve TService from created scope. Just read Consuming a scoped service in a background task
You can write it like this:
services.AddHostedService<MyBackgroundService>();
services.AddScoped<IScopedServicePerMessage, ScopedServicePerMessage>();
...
public class MyBackgroundService : BackgroundService
{
private readonly IServiceProvider _sp;
public MyBackgroundService(IServiceProvider sp)
{
_sp = sp;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
DoWork(stoppingToken);
return Task.CompletedTask;
}
private void DoWork(CancellationToken stoppingToken)
{
while(true)
{
var msg = GetNextMessage();
using (var scope = _sp.CreateScope())
{
var servicePerMessage = scope.ServiceProvider.GetRequiredService<IScopedServicePerMessage>();
servicePerMessage.Handle(msg);
}
}
}
...
}
Regarding this:
It's dangerous to resolve a scoped service from a singleton. It may
cause the service to have incorrect state when processing subsequent requests.
It's about the case when you inject scoped service (ef core dbcontext, for instance) directly into singleton. It's not your case.
The documentation is referring to injecting a scoped service into a singleton service. Since the injection happens at the construction of the singleton object, the scoped service would be provided at that time. This will effectively increase the lifetime of the scoped service to that of a singleton service. This is dangerous because a scoped service lifetime is often chosen explicitly to ensure that the object gets disposed quickly again.
The most common example would be a database context which owns a database connection; you want to make sure that you free up this database connection as soon as possible to free up the resources. But if you injected the context into a singleton service, it would never get disposed.
That however does not mean that there is no way to consume scoped services within a singleton service. This is done by having the singleton service create a service scope from which it can then retrieve singleton services. It’s important though that this service scope is supposed to be short-lived. So take the example from ASP.NET Core itself, where a service scope is created for every request, and do something similar. For example in your case, you could do it for every incoming message if that makes sense for your application.
To create a service scope, you should inject an IServiceScopeFactory; you can then create a scope with it like this:
public async Task Process(TPayload payload)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.GetService<TService>();
await service.Process(payload);
}
}
This pattern is strictly only necessary if you need to consume scoped services. You could resolve all other services directly without creating a scope. If you can reuse the same service instance to process all payloads, you could also inject the service as a singleton (same as registering it as transient but resolving it only once). If you need a fresh instance for every payload, then consider creating a scope even if it isn’t strictly necessary.
First, transient services are not scoped services. Transient services are usually externally owned by your code and are created each time they are resolved from the container. Container does not cache transient services.
TService is going to be registered as Transient ... For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives.
You do not need a scope for resolving transient services. Even if you create a scope, the scope still does not manage / own transient services. That, for example, ending the lifetime of the scope does not end lifetime of transient services.
You could simply use the IServiceProvider injected in QueueListener to resolve TService. And each TService resolved should be already like what you want
lightweight and stateless as a handler for payload
With regards to
Documentation says:
What the document says might not be relevant now since you are not using scoped services. But in case you want to know the reason:
It's dangerous to resolve a scoped service from a singleton.
Singleton is a special kind of scope. Singleton services are created and cached within a "root" scope of the container, which is essentially the container itself.
If you resolve scoped service from singleton, the lifetime / scope where the service instance is resolved and cached is likely to be the "root" scope. This leads to a problem where the scoped service instance being cached inside the container, and shared across multiple client requests.
This is dangerous, because scoped services are supposed to be
Scoped lifetime services (AddScoped) are created once per client request (connection).
I don't wanted my singleton class depending on the IServiceProvider directly.
So I've used a custom factory to accomplish this goal.
May this code example help others:
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IScopedBar, ScopedBar>();
services.AddSingleton<IScopedServiceFactory<IScopedBar>, ScopedServiceFactory<IScopedBar>>(
(provider) => {
var scope = provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedBar>();
return new ScopedServiceFactory<IScopedBar>(() => new ScopedService<IScopedBar>(scope, service));
});
services.AddSingleton<ISingletonFoo, SingletonFoo>();
}
// ...
}
public interface ISingletonFoo
{
void DoSomethingUsingScopedServices();
}
public class SingletonFoo : ISingletonFoo
{
private readonly IScopedServiceFactory<IScopedBar> _barFactory;
public SingletonFoo(IScopedServiceFactory<IScopedBar> barFactory)
{
_barFactory = barFactory;
}
public void DoSomethingUsingScopedServices()
{
using var scopedService = _barFactory.CreateService();
scopedService.Service.DoSomething();
}
}
public interface IScopedBar
{
void DoSomething();
}
public class ScopedBar : IScopedBar
{
public void DoSomething()
{
// Do something
}
}
public interface IScopedService<T> : IDisposable
{
T Service { get; }
}
public interface IScopedServiceFactory<T>
{
IScopedService<T> CreateService();
}
public class ScopedService<T> : IScopedService<T>
{
private readonly IDisposable _scope;
public ScopedService(IDisposable scope, T service)
{
_scope = scope;
Service = service;
}
public T Service { get; }
public void Dispose()
{
_scope.Dispose();
}
}
public class ScopedServiceFactory<T> : IScopedServiceFactory<T>
{
private readonly Func<IScopedService<T>> _serviceFactory;
public ScopedServiceFactory(Func<IScopedService<T>> serviceFactory)
{
_serviceFactory = serviceFactory;
}
public IScopedService<T> CreateService()
{
return _serviceFactory();
}
}
Here is my code to consume scoped service:
public interface IScopedResolver<T> where T: class
{
TResult Resolve<TResult>(Func<T, TResult> dataFactory);
Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory);
}
Implement class:
public class ScopedResolver<T> : IScopeResolver<T> where T: class
{
private readonly IServiceProvider _provider;
public ScopedResolver(IServiceProvider provider)
{
_provider = provider;
}
public TResult Resolve<TResult>(Func<T, TResult> dataFactory)
{
using IServiceScope scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return dataFactory(service);
}
public async Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory)
{
using var scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return await dataFactory(service);
}
}
Register at startup:
services.AddSingleton(typeof(IScopedResolver<>), typeof(ScopedResolver<>));
using ScopedResolve:
public class ServiceA
{
private readonly IScopedResolver<DbContext> _context;
public ServiceA(IScopedResolver<DbContext> context)
{
_context = context;
}
public async Task<List<ClassOne>> GetListAsync()
{
return await _context.ResolveAsync(async s => await s.Set<ClassOne>().ToListAsync());
}
}
What's the correct way to register/resolve a DbContext by an Interface?
Context:
I have multiple web services with a different dbcontext each.
Since I need to write some common functionalities in a shared project, I wrote an interface which is implemented by each dbcontext, so I can have the set of shared functionalities "dbcontext-implementation-agnostic" just injecting the dbcontext interface.
In each project, each dbcontext is already used and injected by its implementation, but I would like to use just its interface in the common features project.
So, in each webservice I would have something like:
services.AddDbContext<IMyDbcontext, MyDbcontext>(options =>
{
options.UseSqlServer(configuration.GetConnectionString("MyDbContext"));
});
services.AddTransient<ISharedService, SharedService>();
and in the shared project
public class SharedService : ISharedService
{
public SharedService(IMydbcontext context)
{
[...]
}
[...]
}
having IMyDbcontext just like
public interface IMyDbcontext
{
DbSet<SharedSettings> Settings { get; set; }
int SaveChanges(bool acceptAllChangesOnSuccess);
int SaveChanges();
Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default);
Task<int> SaveChangesAsync(CancellationToken cancellationToken = default);
}
and I require ISharedService within Startup.cs - Configure() with
app.ApplicationServices.GetService<ISharedService>();
I've been googling a lot and tried different approches but I couldn't find one which worked...
I've tried to use a forwarder, like this: (as suggested here)
services.AddDbContext<MyDbContext>();
services.AddScoped<IMyDbContext>(provider => provider.GetRequiredService<MyDbContext>());
But, while I've no issues resolving the concrete MyDbContext, I get this error whenever I try to resolve IMyDbContext
System.InvalidOperationException: 'Cannot resolve
'Blah.IMyService' from root provider because it
requires scoped service
'Blah.IMydbcontext'.'
where IMyService is registered as transient and it's implementation constructor is
public MyService(IMydbcontext context)
I also tried to register the dbcontext like this
services.AddDbContext<IMyDbContext, MyDbContext>();
but then, when I try to resolve MyDbContext i get null, and I can't understand why
As pointed out by /u/AngularBeginner:
The IMyService requires a scoped IMyDbContext, but within your
startups Configure() method you don't have a scope.
You can make your IDbContext transient (if that's acceptable to
you).
Alternatively you can try to create a temporary scope using the
CreateScope() method, then get your IMyService from that new scoped
provider.
Source
So, I created a scope with IServiceScopeFactory and it did the trick
var scopeFactory = app.ApplicationServices.GetService<IServiceScopeFactory>();
using var scope = scopeFactory.CreateScope();
var myService = scope.ServiceProvider.GetService<IMyService>();
On an ASP.NET Core project I have the following on Startup:
services.AddDbContext<Context>(x => x.UseSqlServer(connectionString));
services.AddTransient<IValidationService, ValidationService>();
services.AddTransient<IValidator<Model>, ModelValidator>();
The ValidationService is as follows:
public interface IValidationService {
Task<List<Error>> ValidateAsync<T>(T model);
}
public class ValidationService : IValidationService {
private readonly IServiceProvider _provider;
public ValidationService(IServiceProvider provider) {
_provider = provider;
}
public async Task<List<Error>> ValidateAsync<T>(T model) {
IValidator<T> validator = _provider.GetRequiredService<IValidator<T>>();
return await validator.ValidateAsync(model);
}
}
And the ModelValidator is as follows:
public class ModelValidator : AbstractValidator<Model> {
public ModelValidator(Context context) {
// Some code using context
}
}
When I inject a IValidationService in a controller and use it as:
List<Error> errors = await _validator.ValidateAsync(order);
I get the error:
System.ObjectDisposedException: Cannot access a disposed object. A
common cause of this error is disposing a context that was resolved
from dependency injection and then later trying to use the same
context instance elsewhere in your application. This may occur is you
are calling Dispose() on the context, or wrapping the context in a
using statement. If you are using dependency injection, you should
let the dependency injection container take care of disposing context
instances. Object name: 'Context'.
Any idea why I am having this error when using Context inside ModelValidator.
How to fix this?
UPDATE
So I changed the code to:
services.AddScoped<IValidationService, ValidationService>();
services.AddScoped<IValidator<Model>, ModelValidator>();
But I get the same error ...
UPDATE - Seed Data Code inside Configure method on Startup
So on Configure method I have:
if (hostingEnvironment.IsDevelopment())
applicationBuilder.SeedData();
And the SeedData extension is:
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static void SeedData(this IApplicationBuilder builder) {
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
What am I missing?
UPDATE - A possible solution
Changing my Seed method to the following seems to work:
using (IServiceScope scope =
_provider.GetRequiredService<IServiceScopeFactory>().CreateScope()) {
Context context = _provider.GetService<Context>();
// Insert data in database
}
Just a guess in what causes your error:
You are using DI and async calls. If, somewhere in your call stack, you return a void instead of Task, you get the described behavior. At that point, the call is ended and the context disposed. So check if you have an async call that returns a void instead of Task. If you change the return value, the ObjectDisposedException is probably fixed.
public static class DataSeedExtensions {
private static IServiceProvider _provider;
public static async Task SeedData(this IApplicationBuilder builder) { //This line of code
_provider = builder.ApplicationServices;
_type = type;
using (Context context = (Context)_provider.GetService<Context>()) {
await context.Database.MigrateAsync();
// Insert data code
}
}
And in configure:
if (hostingEnvironment.IsDevelopment()){
await applicationBuilder.SeedData();
}
Blog post on how to fix this error: Cannot access a disposed object in ASP.NET Core when injecting DbContext
I had a similar issue working with asp.net core. I have an async POST method in my controller and when it returns void I will have this exception. After I changed the POST method return a TASK the problem was solved.
Change from:
public async void PostAsync([FromBody] Model yourmodel)
To
public async Task PostAsync([FromBody] Model yourmodel)
Update for ASP.NET Core 2.1
In ASP.NET Core 2.1 the methods changed slightly. The general method is similar to the 2.0, just the methods name and return types have been changed.
public static void Main(string[] args)
{
CreateWebHostBuilder(args)
.Build()
.Seed();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args)
{
return new WebHostBuilder()
...; // Do not call .Build() here
}
Applies for ASP.NET Core 2.0
With ASP.NET Core 2.0 there have been some changes in how EF Core tools (dotnet ef migrations etc.) determine the DbContext and connection string at design time.
The below answer leads that the migrations and seeding are applied when calling any of the dotnet ef xxx commands.
The new pattern for getting a design time instance for the EF Core tools is by using an BuildHostWeb static method.
As per this announcement, EF Core will now use the static BuildWebHost method which configures the whole application, but doesn't run it.
public class Program
{
public static void Main(string[] args)
{
var host = BuildWebHost(args);
host.Run();
}
// Tools will use this to get application services
public static IWebHost BuildWebHost(string[] args) =>
new WebHostBuilder()
.UseKestrel()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseStartup<Startup>()
.Build();
}
Replace this in your old Main method
public static void Main(string[] args)
{
var host = BuildWebHost(args)
.Seed();
host.Run();
}
Where Seed is an extension method:
public static IWebHost Seed(this IWebHost webhost)
{
using (var scope = webhost.Services.GetService<IServiceScopeFactory>().CreateScope())
{
// alternatively resolve UserManager instead and pass that if only think you want to seed are the users
using (var dbContext = scope.ServiceProvider.GetRequiredService<ApplicationDbContext>())
{
SeedData.SeedAsync(dbContext).GetAwaiter().GetResult();
}
}
}
public static class SeedData
{
public static async Task SeedAsync(ApplicationDbContext dbContext)
{
dbContext.Users.Add(new User { Id = 1, Username = "admin", PasswordHash = ... });
}
}
Old Answer, still applies to ASP.NET Core 1.x
There is a semi-official pattern on how to seed Entity Framework Core in ASP.NET Core application you should apply, because during application startup there is no Request and hence no RequestServices (which resolves scoped services).
In essence it boils down to creating a new scope, resolve the types you need and dispose the scope again once you're finished.
// serviceProvider is app.ApplicationServices from Configure(IApplicationBuilder app) method
using (var serviceScope = serviceProvider.GetRequiredService<IServiceScopeFactory>().CreateScope())
{
var db = serviceScope.ServiceProvider.GetService<AppDbContext>();
if (await db.Database.EnsureCreatedAsync())
{
await SeedDatabase(db);
}
}
One of the reasons directly resolving a service via app.ApplicationServices.GetService<MyService>() is that ApplicationServices is the application (or lifetime) scope provider and the services resolved here stay alive until the application is shut down.
Usually the scoped container will resolve from it's parent container, if the object already exists there. So if you instantiate the DbContext this way in the application, it will be available in ApplicationServices container and when a request happens, a child container will be created.
Now when resolving the DbContext it won't be resolved as scoped, because it already exists in the parent container, so the instance of the parent container will be returned instead. But since it has been disposed during the seeding, it won't be accessible.
A scope container is nothing else then a singleton container with limited lifetime.
So never resolve scoped services in Application startup w/o using the pattern above of first creating a scope and resolving from it.
If you are using any async void please replace it with async Task
Had the same issue. Hope this helps someone. In addition to making the method async and return a Task, you need to make sure that the method will also be awaited wherever you are calling it.
the problem is that DBContext is scoped per request by default, but you have things that depend on it scoped as transient, so they do not have the same scope and DBContext may be disposed before you are done using it
Similar to Yang Zhang, I had to change my controller function
From:
public IActionResult MyFunc([FromBody]string apiKey)
To:
public async Task<IActionResult> MyFunc([FromBody]string apiKey)
I'd like to share my solution for those who are trying to start a background task in their controllers. That means you want to start a task and don't want to wait for the result like audit logging to database. If you are creating a task and try to do database operations in that task you will receive this error;
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.\r\nObject name: 'DBContext'.
Already explained in details. Find it here
In my case, it wasn't an Async problem, but the code had a
using (DataContext dc=dataContext) {}
block, and of course, the context was disposed after that.
In my case the controller method was async and it was returning a task but inside that I had 2 await calls. First await calls gets some data from a service and second await call writes to the DB using EF. I had to remove the await from this second call and only then it worked. I didn't remove async/await from method signatures. I just called the second method without await.
I was facing a similar error and later was able to resolve it.
I was calling the async method without using await.
old code
var newUser = _repo.Register(newUserToCreate);
with the fix made
var newUser = await _repo.Register(newUserToCreate);