ObjectDisposedException when returning AsAsyncEnumerable() - c#

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.

Related

EntityFrameworkCore swap DbConnection (add Transaction)

I have large DB model (hundred of tables) split to multiple EntityFrameworkCore DbContexts. Is is a quite common use case when I modify multiple entities in two (or more) different DbContexts, but I need to commit this operations withing a single transaction.
I use a IReporitory pattern where I get injected into Controller an instance of ISomeRepository implementation which looks like:
[HttpPost]
public asycn Task DoSomeWorkAsync()
{
using (var transaction = this.IEmployeesRepository.BeginTransaction())
{
// do some work
await this.IEmployeesRepository.SaveChangesAsync();
// do another work
await this.IPayrollRepository.SaveChangesAsync();
}
}
An EmployeeDbContext implements an IEmployeeRepository interface, PayrollDbContexts implements IPayrollRepository.
I end up with error:
System.InvalidOperationException: The specified transaction is not associated with the current connection. Only transactions associated with the current connection may be used.
There exists very handy documentation, which basically solves the problem.
Cool, but I am not able to create a new instance of EmployeeDbContext, as described in documentation, because I am working with abstraction - interface only. I am looking for some method how to change / swap / inject / replace a DbConnection in existing DbContext.
I was thinking of implementing Clone method like
[HttpPost]
public asycn Task DoSomeWorkAsync()
{
using (var transaction = this.IEmployeesRepository.BeginTransaction())
{
await this.IEmployeesRepository.SaveChangesAsync();
var payrollRepoClone = IPayrollRepository.Clone(transaction);
await payrollRepoClone.SaveChangesAsync();
}
}
and then I would do
public class PayrollDbContext : DbContext, IPayrollRepository
{
private readonly DbConnection dbConnection;
public PayrollDbContext Clone(DbTransaction tran)
{
return new PayrollDbContext(tran.GetDbTransaction.Connection);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseSqlServer(dbConnection);
}
}
but I am trying to avoid this kind of tight coupling with SQL Server, because currently UseNpgSql is called in IoC in Service container where I resolve IPayrolRepository instance. And UseInMemoryDatabase in unit tests. This would crash my tests (or at least will require some dirty if/else in OnConfiguring)
Do you have any hints how to inject transaction or dbConnection to existing DbContext?
Thanks

How to dispose the ContextDB class automatically after any query done using it

ASP.NET MVC 5 Project.
I know that the best practice of using EF context object as the following
using(var context = new ContextDB())
{
}
But I am working with a large existing project which not used this practice.
the project using the following pattern
public abstract class BaseService
{
private static ContextDB _data { get; set; }
public static ContextDB Data
{
get
{
if (_data== null)
_data= new ContextDB();
return _data;
}
}
}
Actually, because of this pattern, I am receiving this exception (sometimes, not always)
So to solve this I have to change all the code which is using the shared Data
property and replace it with the new instance of ContextDB as I mentioned in the beginning of the question.
The problem that this is a very large modification, and I will not be allowed to do that modification.
The Question, can I solve this problem without changing a ton of code, In another word, can I solve the problems with modifications done only inside the BaseService class, for example, Is there any event which I could handle to know if any query is executed and then dispose of the ContextDB
here is the pseudo-code of the idea in my mind
public abstract class BaseService
{
public static ContextDB Data
{
get
{
ContextDB _data= new ContextDB();
_data.SqlQueryExecuted += () => { this._data.dispose(); }
return _data;
}
}
}
NOTE: the SaveChanged event is not suitable, because not all of the query are updating or inserting.
I may use following solution.
In Global.asax
Begin Request : Create Instance of your dbContext. Store it in HttpContext.Current.Items.
End Request : Grab the context and close / dispose connection.
Another better solution is to use DI. Dependency Injection and limit the scope of your instance. There are many way Like Singleton, PerRequest etc.

Disposing EF DBContext in MVC controller

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.

Console app with MVC, Ninject and WCF Service (Dispose issue?)

I have a MVC application with all Ninject stuff wired up properly. Within the application I wanted to add functionality to call a WCF service, which then sends bulk messages (i.e. bulk printing) to RabbitMQ queue .
A 'processor' app subscribes to messages in the queue and process them. This is where I also want to update some stuff in the database, so I want all my services and repositories from the MVC app to be available too.
The processor app implements the following:
public abstract class KernelImplementation
{
private IKernel _kernel;
public IKernel Kernel
{
get
{
if (_kernel != null)
return _kernel;
else
{
_kernel = new StandardKernel(new RepositoryModule(),
new DomainModule(),
new ServiceModule(),
new MessageModule());
return _kernel;
}
}
}
}
All Ninject repository bindings are specified within RepositoryModule, which is also used within MVC app and look like this:
Bind<IReviewRepository>().To<ReviewRepository>().InCallScope();
The processor class
public class Processor : KernelImplementation
{
private readonly IReviewPrintMessage _reviewPrintMessage;
public Processor()
{
_reviewPrintMessage = Kernel.Get<IReviewPrintMessage>();
[...]
_bus.Subscribe<ReviewPrintContract>("ReviewPrint_Id",
(reviewPrintContract) => _reviewPrintMessage.ProcessReviewPrint(reviewPrintContract));
//calling ProcessReviewPrint where I want my repositories to be available
}
}
Everything works fine until I update the database from the MVC app or database directly. The processor app doesn't know anything about those changes and the next time it tries to process something, it works on a 'cached' DbContext. I'm sure it's something to do with not disposing the DbContext properly, but I'm not sure what scope should be used for a console app (tried all sort of different scopes to no avail).
The only solution I can think of at the moment is to call WCF service back from the processor app and perform all the necessary updates within the service, but I would want to avoid that.
UPDATE: Adding update logic
Simplified ReviewPrintMessage:
public class ReviewPrintMessage : IReviewPrintMessage
{
private readonly IReviewService _reviewService;
public ReviewPrintMessage(IReviewService reviewService)
{
_reviewService = reviewService;
}
public void ProcessReviewPrint(ReviewPrintContract reviewPrintContract)
{
var review =
_reviewService.GetReview(reviewPrintContract.ReviewId);
[...]
//do all sorts of stuff here
[...]
_reviewService.UpdateReview(review);
}
}
UpdateReview method in ReviewService:
public void UpdateTenancyAgreementReview(TenancyAgreementReview review)
{
_tenancyAgreementReviewRepository.Update(review);
_unitOfWork.Commit();
}
RepositoryBase:
public abstract class EntityRepositoryBase<T> where T : class
{
protected MyContext _dataContext;
protected EntityRepositoryBase(IDbFactory dbFactory)
{
this.DbFactory = dbFactory;
_dbSet = this.DataContext.Set<T>();
}
[...]
public virtual void Update(T entity)
{
try
{
DataContext.Entry(entity).State = EntityState.Modified;
}
catch (Exception exception)
{
throw new EntityException(string.Format("Failed to update entity '{0}'", typeof(T).Name), exception);
}
}
}
Context itself is bound like this:
Bind<MyContext>().ToSelf().InCallScope();
From the description of scopes I thought that Transient scope was the right choice, but as I said earlier I tried all sorts including RequestScope, TransientScope, NamedScope and even Singleton (although I knew it wouldn't be desired behaviour), but none of them seem to be disposing the context properly.
What you'll need is one DbContext instance per transaction.
Now other "applications" like web-applications or wcf-service may be doing one transaction per request (and thus use something like InRequestScope(). Also note, that these application create an object graph for each request. However, that is a concept unknown to your console application.
Furthermore, scoping only affects the instantiation of objects. Once they are instantiated, Scoping does not have any effect on them.
So one way to solve your issue would be to create the (relevant) object tree/graph per transaction and then you could use InCallScope() (InCallScope really means "once per instantiation of an object graph", see here).
That would mean that you'd need a factory for IReviewPrintMessage (have a look at ninject.extensions.factory) and create an instance of IReviewPrintMessage every time you want to execute IReviewPrintMessage.ProcessReviewPrint.
Now you have re-created the "per request pattern".
However, regarding CompositionRoot this is not recommended.
Alternative: you can also only re-create the DbContext as needed. Instead of passing it around everywhere (DbContext as additional parameter on almost every method) you use a SynchronizationContext local storage (or if you don't use TPL/async await: a ThreadLocal). I've already described this method in more detail here

Are you supposed to wrap Unit of Work?

I've been attempting to educate myself on the whole EF/Repository/Unit of Work architecture- it's going slowly.
Anyway, I've managed to create some repositories and a Unit of Work which use interfaces. (Took me an hour to make it work with interfaces when it was working perfectly without).
Right.. to the question. Is there any point of wrapping the Unit of Work here?
private readonly IUnitOfWork _unitOfWork;
public ActionResult Index()
{
var all = _unitOfWork.CampaignRepository.All().ToList();
return View(all);
}
Or
public ActionResult Index()
{
using (var uow = _unitOfWork)
{
all = uow.CampaignRepository.All().ToList();
return View(all);
}
}
Also- It is instantiated in the controller
public CampaignController(MarketingContext context)
{
_unitOfWork = new UnitOfWork(context);
}
Does this mean it's always available within the controller? Or only when the Unit of Work is specifically called?
Sorry for any naivety, but I've been reading so many different ways of doing everything!
first of all, as you are using entity framework then you don't need to implement UnitOfWork pattern because EF supports for UOF by default.(SaveChanges of EF support UOF)
now if you want to implement the UnitOfWork don't take all of you entities of repository by using ToList(), because you don't need to make operation on your data whenever you call the controller(it's a performance issue), for better implementation of UnotOfWork and Repository pattern take a look at this article.
I would never wrap a field inside a using statement because then the field could conceivably be in scope elsewhere, but already disposed. Normally you should instantiate the disposable object within the using clause:
using(var scope = new TransactionScope())
{
//etc
}
Override the Dispose method in the controller and call dispose on your UOW there:
protected override void Dispose(bool disposing)
{
if (disposing)
_unitOfWork.Dispose();
base.Dispose(disposing);
}
Or you can specify the scope in an IoC container as suggested in the comment by elolos

Categories

Resources