I am seeing a The operation cannot be completed because the DbContext has been disposed. exception message even though the code is using eager loading by returning List<T>. I have the following code in an EntityRepository class:
public List<Entity> GetEntityByProperty(string property)
{
using (AppDbContext appDbContext = GetContext())
{
List<Entity> entities = appDbContext.Entities
.Where(e => e.Property == property)
.ToList();
return entities;
}
}
This is in turn being called from a web service class:
private static EntityRepository EntityRepositoryStatic = new EntityRepository();
[WebMethod]
public List<Entity> GetEntityByProperty(string property)
{
return EntityRepositoryStatic.GetEntityByProperty(property);
}
The EntityRepository inherits from a Repository base class which implements IDisposable (to dispose the context) and has this code to return an instance of the context:
private AppDbContext appDbContext;
public AppDbContext GetContext()
{
if (appDbContext == null)
{
appDbContext = new AppDbContext();
}
return appDbContext;
}
The exception is thrown on the line making the call to appDbContext in the repository. Am I missing something obvious?
This is a large legacy codebase therefore we are simply trying to replace inline calls to the context in the web service with calls to the repository classes instead.
The first time you call GetContext() you create a new context, then you return it, then it's used and disposed of. Then the next call to GetContext() returns the original, already disposed, context, and you get an error. If you want to re-use it, you can't dispose it; if you want to dispose of it when done, you can't re-use it.
Related
In my .NET Core 3 WebAPI project, I have the following, simple method call:
[HttpGet("ViewerRoles")]
public IAsyncEnumerable<ViewerRole> GetViewList() {
using var db = new MpaContext();
return db.ViewerRoles.AsAsyncEnumerable();
}
This throws me an ObjectDisposedException. AsAsyncEnumerable() is relatively new and I can't find any appropriate examples of how to use it in such situations. Should I just remove the using keyword and the Entity Framework database connection magically disposes itself? Or is there another trick?
You have two options. Either enumerate the IAsyncEnumerable inside your GetViewList method:
[HttpGet("ViewerRoles")]
public async IAsyncEnumerable<ViewerRole> GetViewList()
{
using var db = new MpaContext();
await foreach (var item in db.ViewerRoles.AsAsyncEnumerable().ConfigureAwait(false))
{
yield return item;
}
}
...or install the System.Interactive.Async package and use the static AsyncEnumerableEx.Using method:
[HttpGet("ViewerRoles")]
public IAsyncEnumerable<ViewerRole> GetViewList()
{
return AsyncEnumerableEx.Using(() => new MpaContext(),
db => db.ViewerRoles.AsAsyncEnumerable());
}
Here is the signature of the AsyncEnumerableEx.Using method:
public static IAsyncEnumerable<TSource> Using<TSource, TResource>(
Func<TResource> resourceFactory,
Func<TResource, IAsyncEnumerable<TSource>> enumerableFactory)
where TResource : IDisposable;
Unfortunately it seems that there is no online documentation available for this library.
You should implement IDisposable on your controller, and dispose the DbContext in the controller's Dispose method
using statement in this case scopes your dbContext to function scope, so the correct way would be to enumerate before returning from action, otherwise you returning something what is cannot be correctly evaluated later (after function returned and context disposed)
alternatively, you can move dbContext creation to controller's scope, which is can be request scope (it is not that hart to implement via framework's DI and DI will take care about everything with IDisposable and scoped to request "magically")
I have also found the approach to use the method Response.RegisterForDispose(). But I still do not know which approach is the most promising.
[HttpGet("ViewerRoles")]
public IAsyncEnumerable<ViewerRole> GetViewList() {
MpaContext db = new MpaContext();
Response.RegisterForDispose(db);
return db.ViewerRoles.AsAsyncEnumerable();
}
I see all answers have its own point and all of them true. So, I can only make some clarifications on them.
You should choose a moment of disposing object depending on how you expose it. For example, your original code implicitly exposes MpaContext db to AspNet pipeline and you can't dispose db until netcore done his work with it. So, you can register disposing of it by Response.RegisterForDispose(), as you mentioned. But, it's uncommon because you don't have access to Response usually - you can do this only inside Controller, or if you share it with Controller dependencies, but it will rise code complexity.
That's why you can avoid this by relying on lifetime of controller. Since, it's in request scope it will live until response was sent. So, you can create your db as controller dependency and hold it within a property. Also you should implement IDisposable on controller.
public class RoleController : IDisposable
{
private MpaContext DbContext { get; }
public RoleController()
{
DbContext = new MpaContext();
}
[HttpGet( "ViewerRoles" )]
public IAsyncEnumerable<ViewerRole> GetViewList()
{
return DbContext.ViewerRoles.AsAsyncEnumerable();
}
public void Dispose()
{
DbContext.Dispose();
}
}
In this case you can stick to this pattern even if you will move your logic to some other class (as it supposed to be, I believe). But still, if you manually create disposable objects you should care about disposing them. That's the moment when DI comes to help.
By using DI you can forget about disposing objects that was created by DI. DI will call Dispose() on any dependency when lifecycle of it ends. Register your MpaContext by calling AddDbContextPool<MpaContext>() or AddDbContext<MpaContext>() if you use EntityFramework under MpaContext. With this approach you will get clear code of your controller.
public class RoleController
{
private MpaContext DbContext { get; }
public RoleController( MpaContext dbContext )
{
DbContext = dbContext;
}
[HttpGet( "ViewerRoles" )]
public IAsyncEnumerable<ViewerRole> GetViewList()
{
return DbContext.ViewerRoles.AsAsyncEnumerable();
}
}
If you don't want to expose MpaContext to controller and want to create it manually inside GetViewList(), you can still enumerate result within the action method and dispose context as Theodor Zoulias answered. But why would you, if you can simply delegate this work to DI.
I have a command-line program which is importing data into my system. Because it's inserting data into many tables, I require change tracking. To try and prevent the job from slowing down over time, I have used Autofac (my dependency injection framework) to create an inner lifetime scope from which I resolve dependencies. At the end of each batch, I recreate the lifetime scope and get new instances of my dependencies. The problem is that when I do this, my DataContext which the UnitOfWork depends on is not being refreshed each time, leading to a situation where the job slows down and eventually terminates before completing.
I can see this when debugging by setting the 'Make Object ID' on my DbContext, e.g.
After each batch, the object ID remains $2, demonstrating that the DataContext instance is not getting a new instance. Why is it not getting a new instance?
My code looks something like this:
foreach (var batch in data.Batch(10))
{
using (var scope = LifetimeScope.BeginLifetimeScope(b =>
{
b.RegisterType<UnitOfWork>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterType<MyService1>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterType<MyService2>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
b.RegisterGeneric(typeof(EntityBaseRepository<>)).As(typeof(IEntityBaseRepository<>)).InstancePerLifetimeScope();
}))
{
UnitOfWork = scope.Resolve<IUnitOfWork>();
MyService1 = scope.Resolve<IMyService1>();
MyService2 = scope.Resolve<IMyService2>();
Thing1Repository = scope.Resolve<IEntityBaseRepository<Thing1Repository>>();
Thing2Repository = scope.Resolve<IEntityBaseRepository<Thing2Repository>>();
foreach (var row in batch)
{
try
{
ParseRow(row);
}
catch (Exception e)
{
JobLogger.Error(e, "Failed to parse row. Exception: " + e.Message);
throw;
}
}
}
}
It was my understanding that when I get a new instance of my dependencies, the child dependencies will get new instances as well? Why is the original DataContext still hanging about?
My UnitOfWork looks like this:
public class UnitOfWork : Disposable, IUnitOfWork
{
private readonly IDbFactory _dbFactory;
private DataContext _dbContext;
public UnitOfWork(IDbFactory dbFactory)
{
_dbFactory = dbFactory;
}
public DataContext DbContext => _dbContext ?? (_dbContext = _dbFactory.Initialise());
public void Commit()
{
DbContext.Commit();
}
}
My DbFactory is responsible for creating a new instance of my DataContext:
public class DbFactory : Disposable, IDbFactory
{
DataContext _dbContext;
public DbFactory()
{
_dbContext = new DataContext();
}
public DataContext Initialise()
{
return _dbContext ?? (_dbContext = new DataContext());
}
protected override void DisposeCore()
{
_dbContext?.Dispose();
}
}
My services are registered by scanning the assembly when the program first starts by calling this method:
AutofacConfig.InitialiseJobRunner();
Inside this method, I register my types like this:
builder.RegisterType<DataContext>().AsSelf().InstancePerMatchingLifetimeScope(lifetimeScope);
builder.RegisterGenericInstance(typeof(EntityBaseRepository<>), typeof(IEntityBaseRepository<>), lifetimeScope);
builder.RegisterAssemblyInterfaces(Assembly.Load(Data), lifetimeScope);
RegisterAssemblyInterfaces is implemented as:
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
return builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(lifetimeScope);
}
As you register assembly interfaces like below
public static IRegistrationBuilder<object, ScanningActivatorData, DynamicRegistrationStyle>
RegisterAssemblyInterfaces(this ContainerBuilder builder, Assembly assembly, object lifetimeScope)
{
return builder.RegisterAssemblyTypes(assembly)
.AsImplementedInterfaces()
.InstancePerMatchingLifetimeScope(lifetimeScope);
}
my guess is that your DbFactory is also registered this way. In such case (according to: https://autofac.readthedocs.io/en/latest/lifetime/instance-scope.html#instance-per-matching-lifetime-scope ) you will get the same instance of your factory as your child scope is not named. Try to add
b.RegisterType<DbFactory>.AsImplementedInterfaces().PropertiesAutowired().InstancePerLifetimeScope();
in your loop or change DbFactory to always return new context in Initialise method instead returning the same if it's already instantiated.
I want to make sure I'm disposing my EF dbContext objects.
Currently I'm using static methods to invoke EF crud operations to keep all the data layer stuff black boxed and out of the controllers.
The example below I have one method the returns an IQueryable and uses a Using statment which causes an exception when the query tries to run on a disposed context object.
The other doesn't use the Using statement and works fine but is it getting disposed?
Should I just return IEnumerable instead of IQueryable?
public class MyContext : DbContext
{
public MyContext() : base("MyConnectionString")
{
Database.SetInitializer<EFContext>(null);
}
public DbSet<User> Users { get; set; }
public DbSet<Role> Roles { get; set; }
}
public class Data
{
// Fails when IQuerable tried to run against a disposed MyContext object
public static T Get<T>(params string[] joins)
{
using (var context = new MyContext())
{
return context.Get<T>(joins);
}
}
// Works fine but when is it disposed?
public static T Get<T>(params string[] joins)
{
return new MyContext().Get<T>(joins);
}
}
public ActionResult GetUser(int id = 0)
{
var data = Data.Get<User>();
return View("Users", model);
}
The issue is not about using IQueryable or IEnumerable. You have the exception because by returning IQueryable or IEnumerable, you're not executing the LINQ. You just openned a connection to your Database then close after configuring your query.
To solve this, you need to execute the LINQ query by calling ToList, ToArray, Single[OrDefault], First[OrDefault] etc... extension methods.
Because you're using Web Application, it is a best practice to have one instance of your DbContext in the whole life of your web request. I recommend you to use DI (Dependency Injection), it will help you a lot. You can try Simple Injector which is very simple DI.
If you're not able to use DI so just follow this steps but take a time to learn about DI please :) :
When a web request arrives, store a new instance of your DbContext into HttpContext.Items collections.
In your methods, just retrieve the stored DbContext from HttpContext.Items and use it.
When the web request is terminating, just dispose the DbContext.
I'm registering a DbContext onto the TinyIoCContainer that's passed into the ConfigureRequestContainer method on the DefaultNancyBootstrapper.
Whilst this works fine, I've noticed that the Dispose method on the context is never called once a request has completed. I'd expect the DbContext to be disposed of after a request to close the connection (we're using SQLite).
Q: Are disposable instances actually disposed at the end of a request within the TinyIoCContainer?
Bootstrapper
protected override void ConfigureRequestContainer(TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
container.Register<IContext>((_,__) =>
{
// Code here to get connection string
return new Context(new SQLiteConnection(connString), true);
});
}
Context
public interface IContext : IDisposable
{
...
}
public class Context : DbContext, IContext
{
...
public new void Dispose()
{
base.Dispose(); // This never gets called
}
}
Update
The marked answer was ultimately correct. I basically had to do something like so:
if (string.IsNullOrEmpty(context.Request.UserHostAddress))
{
container.Register<IContext>((_,__) => null);
}
else
{
// Get username from request headers
// Build up SQLite connection string based off username
var dbContext = new Context(new SQLiteConnection(connString));
container.Register<IContext>(dbContext);
}
I think its because you're using the manual factory registration, it expects you to control lifetime yourself. You probably don't want to be using that anyway, as you are creating a new context every time you ask for one with the code you have there - switch it to an instance registration and you should be ok.
container.Register<IContext>(new Context(new SQLiteConnection(connString), true));
Haven't used TinyIoC a lot but this page says that registration per request is different, not sure if that should always be the case.
https://github.com/grumpydev/TinyIoC/wiki/Registration---lifetimes
I am not at all sure what the underlying problem is that is causing this exception.
I am using ASP.NET MVC, with Unity.Mvc, and Entity Framework 6. I have the following code to register my repositories:
public static void RegisterTypes(IUnityContainer container)
{
// NOTE: To load from web.config uncomment the line below. Make sure to add a Microsoft.Practices.Unity.Configuration to the using statements.
// container.LoadConfiguration();
// TODO: Register your types here
// container.RegisterType<IProductRepository, ProductRepository>();
container.RegisterType<IGenericRepository<Customer>, GenericRepository<Customer>>();
container.RegisterType<IGenericRepository<Product>, GenericRepository<Product>>();
container.RegisterType<IGenericRepository<Order>, GenericRepository<Order>>();
container.RegisterType<IGenericRepository<OrderItem>, GenericRepository<OrderItem>>();
container.RegisterType<IGenericRepository<Supplier>, GenericRepository<Supplier>>();
}
And then in a controller I have:
public class IndexController : Controller
{
public IndexController(IGenericRepository<Customer> testGenericRepository)
{
var result = testGenericRepository.SelectAll();
}
public ActionResult Index()
{
return View();
}
}
And the repository has the following code:
public class GenericRepository<T> : IGenericRepository<T>
where T : class
{
private readonly DbContext _dbContext;
private readonly IDbSet<T> _dbSet;
public GenericRepository(DbContext dbContext)
{
if (dbContext == null)
{
throw new ArgumentNullException(nameof(dbContext));
}
_dbContext = dbContext;
_dbSet = _dbContext.Set<T>();
}
public IEnumerable<T> SelectAll()
{
return _dbSet.AsEnumerable<T>();
}
}
The problem that I'm having is that if I have a breakpoint in the "RegisterTypes" method, I can see that the container is definitely getting all the repositories registered, but a breakpoint in the constructor of the repositories never gets hit.
So I think that the fact that the breakpoint does not get hit, and I have not registered a "System.Data.Common.DbConnection" means that the DbContext that the repository uses never gets set.
I can't find any useful information about how to use "System.Data.Common.DbConnection" with Unity and the DbContext from Entity Framework.
How do I resolve this?
You should add to your RegisterTypes how to build your DbContext, and probably with which lifetime.
If you have your own class (say CustomContext) inheriting from DbContext, register it. Supposing your default lifetime is adequate:
container.RegisterType<DBContext, CustomContext>();
If you use directly DbContext, instruct Unity which constructor it should use. By example, supposing your connection string is named appConnectionString:
container.RegisterType<DBContext>(
new InjectionConstructor("name=appConnectionString"));