-
Notifications
You must be signed in to change notification settings - Fork 314
Cannot access a disposed object in ASP.NET Core when injecting DbContext #440
Comments
@javiercn can you take a look to see what's going on here? |
I can't reproduce this. @mdmoura can you provide a small repro project? Here is what I tried. public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
var connectionString = @"Data Source = (localdb)\v11.0; Initial Catalog = PeopleContext; Integrated Security = True; Connect Timeout = 30; Encrypt = False; TrustServerCertificate = True; ApplicationIntent = ReadWrite; MultiSubnetFailover = False";
services.AddDbContext<Context>(c => c.UseSqlServer(connectionString));
services.AddScoped<IValidationService, ValidationService>();
services.AddScoped<IValidator<Person>, ModelValidator>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory, Context ctx)
{
ctx.Database.EnsureCreated();
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
loggerFactory.AddConsole();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
public static void Main(string[] args)
{
var host = new WebHostBuilder()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseIISIntegration()
.UseKestrel()
.UseStartup<Startup>()
.Build();
host.Run();
}
}
public class HomeController : Controller
{
private readonly IValidationService _validationService;
public HomeController(IValidationService validationService)
{
_validationService = validationService;
}
public IActionResult Index()
{
return View();
}
[HttpPost("/Person")]
public async Task<IActionResult> Post([FromBody] Person person)
{
var errors = await _validationService.ValidateAsync(person);
if (errors.Count > 0)
{
return BadRequest(errors);
}
else
{
return Ok(person);
}
}
}
public class Context : DbContext
{
public Context(DbContextOptions<Context> options) : base(options)
{
}
public DbSet<Person> People { get; set; }
}
public class Person
{
public int Id { get; set; }
public string Name { get; set; }
}
public interface IValidationService
{
Task<IList<Error>> ValidateAsync<T>(T model);
}
public class ValidationService : IValidationService
{
private readonly IServiceProvider _provider;
public ValidationService(IServiceProvider provider)
{
_provider = provider;
}
public async Task<IList<Error>> ValidateAsync<T>(T model)
{
var validator = _provider.GetRequiredService<IValidator<T>>();
return await validator.ValidateAsync(model);
}
}
public class ModelValidator : IValidator<Person>
{
private readonly Context _ctx;
public ModelValidator(Context context)
{
_ctx = context;
}
public async Task<IList<Error>> ValidateAsync(Person model)
{
var person = await _ctx.People.FirstOrDefaultAsync(p => p.Id == model.Id);
return person != null ? new List<Error> { new Error("Person already exists") } : Enumerable.Empty<Error>().ToList();
}
}
public interface IValidator<T>
{
Task<IList<Error>> ValidateAsync(T model);
}
public class Error
{
public Error(string description)
{
Description = description;
}
public string Description { get; set; }
} |
@javiercn I found the problem. On Startup I was seeding data to the database as follows:
Where the extension method SeedData is:
Then I changed it to (added the IServiceScope):
Now I do not get the error anymore when injecting Context into a Validator ... Should the 'using (Context ...)' affect the application when running afterwards? This is not something obvious to find ... |
Hmm. Looks like the issue is that you're resolving a "scoped" instance from the top-level provider, which effectively makes it a singleton. Since you've wrapped it in a The reason it doesn't reproduce in @javiercn's sample, is because controllers' dependencies ( I think this has been solved with #430, where the provider will throw if a scoped services is resolved from the application provider. The solution is, as you've already figured out, to create a scope and resolve the context from that, like this: public static class DataSeedExtensions
{
public static void SeedData(this IApplicationBuilder builder)
{
var provider = builder.ApplicationServices;
var scopeFactory = provider.GetRequiredService<IServiceScopeFactory>();
using (var scope = scopeFactory.CreateScope())
using (var context = scope.Provider.GetRequiredService<Context>())
{
// Use context to insert data in database...
}
}
} |
Aparently this error is caused by the readonly property where you store the Context or the Service you injected. |
I have this problem when I make multiple requests at the same time, does anyone have an idea of what it might be?
|
@tiagorosendo And where/how are you getting the |
|
@tiagorosendo And where/how are you getting the |
In controller like this :
|
@tiagorosendo Can you show the registrations? By default, that dependency chain would throw a StackOverflowException, no? You have an |
The Error Message is: "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. |
Hi, I have the following piece of code:
As you see the Users are being fetched by a sub query. The first time the server is called the query returns the list successfully. Subsequent queries will result in the following error:
If I add .ToList() to the subquery everything works just fine. Services that are registered with the app:
Using AspNetCore 2.0.1 including Entity Framework Could you please shed some light on the following? Much appreciated |
In your code, I haven’t see you using “DependencyInjection”, the ApplicationDbContext has been
Cleared when you use it.
Hope my errors experience could help you.
Sent from Mail<https://go.microsoft.com/fwlink/?LinkId=550986> for Windows 10
…________________________________
From: kevinvella <[email protected]>
Sent: Tuesday, June 26, 2018 6:07:40 PM
To: aspnet/DependencyInjection
Cc: DuanShaolong; Comment
Subject: Re: [aspnet/DependencyInjection] Cannot access a disposed object in ASP.NET Core when injecting DbContext (#440)
Hi,
I have the following piece of code:
var request = (from chatRoomUsers in chatRoomUsersRepo.GetAll()
join chatroom in chatRoomRepo.GetAll()
on chatRoomUsers.cru_ChatRoomId equals chatroom.cr_Pk
where chatRoomUsers.cru_UserId == userId
orderby chatroom.cr_CreatedAt descending
select new
{
ChatRoom = chatroom,
ChatRoomData = jsonService.Deserialize<ChatRoomData>(chatroom.cr_Data),
Users = (from users in userRepo.GetAll()
join chatRoomUsers in chatRoomUsersRepo.GetAll()
on users.usr_Pk equals chatRoomUsers.cru_UserId
where chatRoomUsers.cru_ChatRoomId == chatroom.cr_Pk
select users)
}).ToList().Select(x => new ChatRoom
{
Id = x.ChatRoom.cr_Pk,
Type = x.ChatRoom.cr_Type == 1 ? "P2P" : "Group Chat",
Data = new ChatRoomData()
{
Name = x.ChatRoomData.Name != null ? x.ChatRoomData.Name : string.Join(", ", x.Users.Where(z => z.usr_Pk != userId).Select(y => $"{y.usr_Firstname} {y.usr_LastName}").FirstOrDefault()),
ProfilePicURL = x.ChatRoom.cr_Type == 1 ? x.Users.Where(z => z.usr_Pk != userId).FirstOrDefault().usr_ImageProfileUrl : null,
},
Users = x.Users.Select(y => $"{y.usr_Firstname} {y.usr_LastName}").ToList(),
Guid = x.ChatRoom.cr_Guid,
}).ToList();
As you see the Users are being fetched by a sub query. The first time the server is called the query returns the list successfully. Subsequent queries will result in the following 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 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.
Object name: 'ApplicationContext'.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_Database()
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.OnFirstExecution()
at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.Enumerator.MoveNext()
at Microsoft.EntityFrameworkCore.Query.QueryMethodProvider.<_InjectParameters>d__27`1.MoveNext()
at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
at System.Linq.Enumerable.TryGetFirst[TSource](IEnumerable`1 source, Boolean& found)
at lambda_method(Closure , QueryContext )
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass17_1`1.<CompileQueryCore>b__0(QueryContext qc)
at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query)
at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression)
at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
at.Services.App.ChatRoomService.<>c__DisplayClass19_0.<GetByUser>b__6(<>f__AnonymousType45`3 x) in /Users/kevin/Documents/Development///.Services/App/ChatRoomService.cs:line 281
at System.Linq.Enumerable.SelectListIterator`2.ToList()
at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
at .Services.App.ChatRoomService.GetByUser(Int32 userId) in /Users/kevin/Documents/Development///.Services/App/ChatRoomService.cs:line 258
at .Web.Controllers.ChatRoomController.GetChatrooms(String UserEmail) in /Users/kevin/Documents/Development///.Web/Controllers/ChatRoomController.cs:line 289}
If I add .ToList() to the subquery everything works just fine.
Could you please shed some light on the following.
Much appreciated
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub<#440 (comment)>, or mute the thread<https://github.com/notifications/unsubscribe-auth/ASxamuwZWrGHnE23iodaHpM2Y7UJRrnFks5uAgfsgaJpZM4JZyBo>.
|
Have the same issue |
Uh oh!
There was an error while loading. Please reload this page.
On an ASP.NET Core project I have the following on Startup:
The ValidationService is as follows:
And the ModelValidator is as follows:
When I inject a IValidationService in a controller and use it as:
I get the error:
Any idea why I am having this error when using Context inside ModelValidator?
The text was updated successfully, but these errors were encountered: