ObjectDisposedException thrown everytime a "User" (membership) object is updated - entity-framework

I am using asp.net mvc 3, entity framework with structure map for IoC.
I have my own custom membership provider.
I had the same kind of problem when I was injecting the dbcontext object and the membership instances using StructureMap so removed this for my UserRepository.
The UserRepository now has a DBContext property which is initialised in the constructor.
Everytime a new user is created/updated (the method doing this is wrapping the dbcontext object in using statement), the next time the dbcontext is referenced I get ObjectDisposedException thrown.
I don't understand what I am doing wrong. Obviously, the membership provider class isn't instantiating the userRepository object everytime it needs it and when a user is updated, the context is disposed because of the using statement. But I thought this was standard practice?
Any help is greatly appreciated.
EDITED:
There is no complex code really. The set up is:
CustomMembershipProvider overrides the standard Membership provider (config file has got the details).
CustomMembershipProvider uses IUserService object that uses IUserRepository which is implemented by UserRepository that has the DBContext private object at class level.
In the UserRepository, I've got:
public void UpdateUser(User user)
{
using(_db)
{
... code to indicate that the user state has changed
_db.SaveChanges();
}
}
Once this code is run, the request is complete. But when another request is made to retrive role information or read the user from the database (in the UserRepository class), I get the ObjectDisposedException thrown. At this point nothing related to User Or Role works because they use the same UserRepository class whose _db variable has been disposed by UpdateUser.
I initially had StructureMap resolve the DBContext object, the customMembership object etc but have had to since remove it from StructureMap's mapping.

Everytime a new user is
created/updated (the method doing this
is wrapping the dbcontext object in
using statement), the next time the
dbcontext is referenced I get
ObjectDisposedException thrown.
DbContext is a disposable resource(Which implements IDisposable). So when you wrap it in a using block the context is disposed when the control flow goes out of the using block.
using(myContext)
{
//do stuff
}
//try to access myContext after the using block throw ObjectDisposedException
You have to re-design how you use the DbContext and when you are going to dispose it.

Related

Blazor Server App and IDbContextFactory not disposing

I have a blazor server application that needs to indirectly connect to a EF core DB context.
None of the blazor components will directly inject an instance of the dbcontext. I am using mediator which will handle all business operations.
The documentation that I have seen so far recommends using IDbContextFactory. I gave it a try but I am not seeing the DbContext created by the factory being disposed. The services that inject IDbContext are not disposed on page changes nor at any other time.
public class QueryHandler : IQueryHandler<Query, Entity>, IDisposable
{
private readonly DbContext dbContext;
public QueryHandler(IDbContextFactory factory)
{
dbContext = factory.CreateDbContext();
}
public Task Handle(Query query)
{
/// do whatever needs to be done.
}
public void Dispose()
{
dbContext.Dispose(); // <-- Dispose never gets called.
}
}
Am I missing something?
The purpose of using a DbContextFactory is to have a DbContext per method.
Exactly because Blazor doesn't offer useful Scopes to handle this.
public class QueryHandler : IQueryHandler<Query, Entity> //, IDisposable
{
...
public QueryHandler(IDbContextFactory factory)
{
_factory = factory;
}
public Task Handle(Query query)
{
using var dbContext = _factory.CreateDbContext();
/// do whatever needs to be done.
}
//public void Dispose() { }
}
This way the DI container and Factory only manage the configuration of the DbContext. Lifecycle management of the DbContext is manual. The Factory is a simple Transient object, is owns no resources.
Manual management usually is with a using statement or using declaration but Blazor also offers an OwningComponentBase. I don't see it being used much.
In Server the DI container exists for the lifetime of the Hub Session and in WASM the lifetime of the Application. Any service objects created within the container, whether Scoped or Transient, implementing IDisposable, are not Disposed until the DI container itself is destroyed. You don't make clear the scope of QueryHandler, but if it's transient that's bad news. You will keep creating new DBContexts without the old ones being disposed.
The purpose of the DbContextFactory is to create unit of work DbContext instances that are used and then quickly disposed correctly. You need to take this approach because DB access will almost certainly be asynchronous. Use a single context and you will quickly hit the situation where you are awaiting one query to complete while trying to use the same context in another operation.
Henk's answer shows you how to use and consume factory created contexts.

The operation cannot be completed because the DbContext has been disposed in entity

private void FunctionA(int t)
{
var c=Context.Entities.TableStudent.Where(x => x.StudentBranch ==t).ToList();
using(var db=Context.Entities)
{
foreach (var q in c)
{
var cb=Context.Entities.Student...
.
.
.
}
DbContext.Entities.SaveChanges();
}
}
c.ForEach(a => a.Situation=true);
DbContext.Entities.SaveChanges();
}
I take DbContext.Entities.SaveChanges(); line give error.This error is
The operation cannot be completed because the DbContext has been disposed.
How can I do it this error.
.
Ok, first up, continue reading/learning about IDisposable, using() blocks, and also naming conventions. You aren't doing yourself any favors by making your code more difficult to understand trying to remember what "a", "c", etc. are, just to save a few seconds typing out a meaningful name.
I'm rather surprised that the code you pasted actually would compile, but without knowing what "Context" is vs. "DbContext" (namespaces, static classes??)...
You're going to have a class which extends EF's DbContext, I'm going to call it "MyContext".. I.e.
public class MyContext : DbContext
{
}
Inside of this class you'll have DbSets declared, and likely an overridden method OnModelCreating() to handle any non-trivial configuration for your entities.
public class MyContext : DbContext
{
public DbSet<TableStudent> Students{ get; set; }
}
This class should never be marked as "static".
Then, with your code to manipulate students, related entities and/or other entities where you have DbSets in the DbContext, you will want to scope the lifespan of a DbContext and ensure that all operations against those entities happens within that lifespan. This lifespan is bounded by a using() block. Once the code leaves the using block, the DbContext is disposed. This means any lazy load references made by entities will not work.
using (var myContext = new MyContext())
{
var students= myContext.Students.Where(x => x.StudentBranch == studentBranch).ToList();
foreach (var student in students)
{
// .. logic...
student.Situation = true;
}
myContext.SaveChanges();
}
// After this point, it is unwise/unsafe to "use" any reference to students.
Do what you need to do within the scope of the using block. If you need to pass student data outside, such as to return from a method call, copy the values over to a plain 'ol C# object (ViewModel or DTO) and return that. Accessing Entities outside of the DbContext scope will result in errors because the context that the student was loaded under has been disposed. Even in cases where the scope is kept alive (such as using a Static context [bad!] or scoping the context to the web request with an IoC container, you may avoid errors, but introduce unintended performance problems due to lazy loading.
SaveChanges is something that typically only needs to ever be called once within a lifetime scope of a DbContext. When set up to know the relationships between entities, EF will manage associating things like Foreign Keys between entities, even new entities that you create. One common panic point people reach is a chicken & egg scenario where I want to create an entity with children, but the children need the parent ID which won't exist until SaveChanges gets called. As long as the parent and child relationship is mapped properly, EF will resolve this automatically when SaveChanges is called provided the children were added to the parent's children collection. SaveChanges applies to the whole set of operations against entities the DbContext knows about (and their relations) so it's not applied at an entity by entity basis.
This should hopefully get you started on how to incorporate Entity Framework and working with it's disposable nature. DbContexts are designed to be relatively short-lived, constructed and disposed as needed. Normally these will be scoped to live as long as a unit of work, being a web request / action, or similar. Longer-lived DbContexts will result in performance/resource issues due to their tracking and caching nature. (Plus issues when attempting to scope SaveChanges calls, discarding changes, etc.)

registering DbContext with multiple parameters

I'm trying to inject TenantProvider into DbContext
public class AppDbContext : IdentityDbContext<ApplicationUser, ApplicationRole, long>
{
public int? _tenantId;
public ITenantProvider _tenantProvider;
public AppDbContext(
DbContextOptions<AppDbContext> options,
ITenantProvider tenantProvider
)
: base(options)
{
_tenantProvider = tenantProvider;
}
but I don't understand how to register it correctly - if I put the breakpoint in the constructor - tenantProvider is null.
The bit from Startup.cs
services.AddDbContext<AppDbContext>(options => AppDbContextOptionsBuilder.Get());
the next line is required to inject the DbContext into a controller or a service (if I add ServiceLifetime.Scoped as a second parameter to the method above - AddDbContext - the feature doesn't work):
services.AddScoped(p => new AppDbContext(AppDbContextOptionsBuilder.Get(), p.GetService<ITenantProvider>()));
(Entity Framework is a separate project in my solution)
When using .AddScoped method - we can pass TenantProvider into constructor by resolving it using .GetService method.
Does anyone have an idea of how to resolve TenantProvider in .AddDbContext method?
Additional info:
I was trying to replace ITenantProvider in the constructor of DbContext with IHttpContextAccessor - the latter is registered as singleton. But the acessor parameter is still null.
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
I don’t really understand what your AddScoped call is supposed to do. AddDbContext will already register the database context properly with the service collection. So when you resolve the context through dependency injection, additional dependencies will be automatically resolved.
So it should be enough to do this:
services.AddDbContext<AppDbContext>(options => …);
services.AddSingleton<ITenantProvider, TenantProvider>();
And then, you can depend on your AppDbContext using constructor injection, e.g. in your controllers.
Two notes though:
When configuring options, you should modify the passed options object. So you should not just return AppDbContextOptionsBuilder.Get() but instead use the passed options object and edit that.
You should really think about whether your database context having a dependency on your tenant provider is the right thing to do. As per SRP, your database should only do a single thing and that is provide database access.
Depending on how your tenant provider affects your database access, it might make more sense to move this dependency up one level into some service that uses both the database context and the tenant provider to query data in the right way.

Getting user information in DbContext

I am attempting to use EF migrations to build/seed my database but my DbContext is causing an error when I use the update-database command.
The DbContext I have implemented has two constructors: one accepts no arguments and the other accepts an IUserContext interface. The IUserContext interface returns a username as a string. The IUserContext is used in the SaveChanges() method for setting user-related audit fields (ex. CreatedBy, UpdatedBy) common to all of my entities. Depending on the implementation of the IUserContext interface, the username may be retrieved from a HttpContext (in a mvc app), WindowsIdentity (in a console app), etc.
If a user attempts to call SaveChanges on the DbContext and the IUserContext is not set, it throws an exception. Basically, I do not want changes saved in the DbContext if a username cannot be provided by the IUserContext interface for purposes of recording who is making the modifications. If the DbContext is being used just for querying, using the no-args constructor isn't an issue since IUserContext is only used during SaveChanges.
When I attempt to use the update-database command, the DbMigrationsConfiguration is given an instance of the DbContext instantiated using the no-args constructor. Therefore, it throws an exception when it tries to invoke the DbContext's SaveChanges after the Seed() method.
Question: How do I pass an IUserContext to my DbContext so the update-database command will not fail and, more importantly, be able to set the appropriate entity fields to the name of the user seeding the changes? Is there some form of DI or other customization I can perform in the DbMigrationsConfiguration? Should I simply add an IUserContext accessor method to the DbContext so the IUserContext can be set outside of the constructor?
Maybe I should be asking a larger question: What is the best (most generic?) way of relaying user information to a DbContext for the purposes of recording who is making modifications? I gave some thought about moving this logic to the business layer of my application but it seems so convenient to encapsulate it in the SaveChanges of the DbContext (even more so when change tracking is enabled) .
We have a similar situation where we record modified user in SaveChanges() override. What we ended up doing was creating a BaseWrapper and use IoC to fill it. The second part is creating a MigrationsContextFactory that migrations will use to create the context. This class lives in the same folder as our ApplicationDbContext.
using System.Data.Entity.Infrastructure;
namespace MyApp.Data
{
public class MigrationsContextFactory : IDbContextFactory<ApplicationDbContext>
{
public ApplicationDbContext Create()
{
return new ApplicationDbContext(new HttpContextBaseWrapper());
}
}
}
public class HttpContextBaseWrapper : IHttpContextBaseWrapper
{
public string UserName
{
get
{
if (HttpContext.Current == null || HttpContext.Current.User == null)
return string.Empty;
return HttpContext.Current.User.Identity.Name;
}
}
}
https://marazt.wordpress.com/2015/01/09/entity-framework-dbcontext-idbcontextfactory-and-codefirst-migration-problem/

Entity Framework + Autofac - Random errors on save

Using autofac as my IoC framework.
I'd like to be able to set up my DbContext instance in my application's startup.
In my ASP.NET MVC 3 project, I register DbContext instance in Global.asax (PerLifetimeScope). But when I fire up my site on multiple browsers (or multiple tabs) at once, sometimes I get Object reference not set to an instance of an object. or New transaction is not allowed because there are other threads running in the session when I try to save changes back to database. Also I get
ExecuteReader requires an open and available Connection. The connection's current state: Broken. sometimes when I want to read data from database.
the errors seem to pop up randomly and I suspect it has something to do with my context's lifetime scope. here's my DbContext's overriden SaveChange method.
public class MyContext : DbContext
{
public override int SaveChanges()
{
var result = base.SaveChanges(); // Exception here
}
}
Here's how I register my context:
builder.Register(c => new MyContext("SomeConnectionString"))
.InstancePerLifetimeScope();
If I just have one open tab of my site in the browser everything works ok.
Also, It's worth mentioning I have CRUD operations with db every 5-10 seconds in my website by calling a controller method using Ajax.
StackTrace for New transaction is not allowed because there are other threads running in the session:
at System.Data.EntityClient.EntityConnection.BeginDbTransaction(IsolationLevel isolationLevel)
at System.Data.EntityClient.EntityConnection.BeginTransaction()
at System.Data.Objects.ObjectContext.SaveChanges(SaveOptions options)
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProject.Data.MyContext.SaveChanges() in D:\Test.cs
StackTrace for Object reference not set to an instance of an object.:
at System.Data.Objects.ObjectStateManager.DetectConflicts(IList`1 entries)
at System.Data.Objects.ObjectStateManager.DetectChanges()
at System.Data.Entity.Internal.InternalContext.DetectChanges(Boolean force)
at System.Data.Entity.Internal.InternalContext.GetStateEntries(Func`2 predicate)
at System.Data.Entity.Internal.InternalContext.GetStateEntries()
at System.Data.Entity.Infrastructure.DbChangeTracker.Entries()
at System.Data.Entity.DbContext.GetValidationErrors()
at System.Data.Entity.Internal.InternalContext.SaveChanges()
at System.Data.Entity.Internal.LazyInternalContext.SaveChanges()
at System.Data.Entity.DbContext.SaveChanges()
at MyProject.Data.MyContext.SaveChanges() in D:\Test.cs at
Registration of MyContext looks ok. Is it possible that some other service that takes a MyContext is registered as a singleton and being shared across threads?
I had the same issue, sporadic errors related to the DbContext while using Autofac to resolve the DbContext.
{System.Data.EntityCommandExecutionException: An error occurred while executing the command definition. See the inner exception for details.
etc.
{System.NullReferenceException: Object reference not set to an instance of an object.
at System.Data.Objects.ObjectStateManager.DetectConflicts(IList`1 entries)
etc.
I found a class resembling the following in my code. The dependency resolution was occurring within a static method inside of the singleton. The object being resolved had a dependency on the DbContext. I haven't had any additional issues after I found a way to restructure this class so that it was no longer a singleton.
Perhaps you have a similar situation? Another thing to try might be to make your DbContext InstancePerHttpRequest. That could help identify whether this is the issue.
public class Singleton
{
private static Singleton _instance = new Singleton();
private Singleton()
{
}
public static void DoSomething<TSource>(TSource source) where TSource : ISource
{
var items = DependencyResolver.Current.Resolve<IEnumerable<IDbContextConsumer<TSource>>>();
foreach (var item in items)
{
item.Execute(source);
}
}
}