6

I want to share a DB context with another method called from outside (inherited class) without creating a new context unless it is being disposed. I want to check the context is disposed so that I could create new context.

It's rest api. There is a bulk upload for multiple entities and I want to share the transaction so if one fail, it will not be committed to DB

mjwills
  • 23,389
  • 6
  • 40
  • 63
gayan1991
  • 753
  • 2
  • 11
  • 35
  • 1
    Even if you check, it may get disposed **between** the check and you using it. I'd suggest not sharing the context. – mjwills Jul 19 '19 at 03:57
  • @mjwills it's rest api. There is a bulk upload for multiple entities and I want to share the transaction so if one fail, it will not be committed to DB – gayan1991 Jul 19 '19 at 04:02
  • Which IoC container are you using? – mjwills Jul 19 '19 at 04:02
  • 1
    You don't need to check if it's disposed or not because if your `DbContext` is injected by the DI system then you'll never need to dispose it yourself because it will only be disposed at the end of the controller's lifespan (i.e. after the Action completes but before the View renders. Note you should never return an "open" `IQueryable` from a Controller Action). – Dai Jul 19 '19 at 04:09
  • You shouldn't be in this situation, id rethink your design, and especially dont try and cache a context – TheGeneral Jul 19 '19 at 05:04
  • @mjwills It is a web api – gayan1991 Jul 19 '19 at 05:35
  • @Dai I create a DbContext and dispose it once the operation is done for the entity. There is a generic class created to handle entity seprately. Now the problem is I have multiple entities and I need to utilize this generic class to handle manipulate multiple entities within one context before I dispose it – gayan1991 Jul 19 '19 at 05:39
  • if you want to reuse the transaction, do not dispose the context before the whole action is completed. I'd advice against it (an entity handling is isolated enough IMHO), but its certainly possible. You do not want to check wether or not the context has been disposed in your situation, because once it is, you've lost anyways. – DevilSuichiro Jul 19 '19 at 06:18
  • 1
    from what you wrote it is not clear why you need to do anything with the context. bulk upload means you already have all the data in which case you simply create a transaction and chuck it all in, rollback if anything fails. – Andrei Dragotoniu Jul 19 '19 at 07:35

1 Answers1

0

Regardless of the comments questioning design quality, valid scenarios exist were the dbContext could be in a disposed state, such as (not a complete list):

For example (within injected dbContext MVC services):

  1. your service iterates though a lower tier of one-or-more service calls, possibly using asynchronous socket handler on a lower tier API library, with each response using the parent requester dbContext.
  2. Your service calls a database job, (asynchronous task or not).
  3. Exception handling logging to database (if the dbContext is already lost - avoid loss of logging debug details)

Note: Long running processes using dbContext like this should follow good practice of avoiding dbContext bloat such as using AsNoTracking() method were possible - as bloat can quickly become a concern.

Performance consideration: Most trusted option is to recreate the dbContext on each child (api call/async task), but this may incur undesired performance overheads, such as when dealing with 1000's of api iterative calls and atomic unit transactions are not viable.

Solution Tested Using Framework: Entity Type: Microsoft.EntityFrameworkCore.DbContext Version=5.0.16.0, Culture=neutral, PublicKeyToken=adb9793829ddae60

Warnings: Lots of warning advice available on this type of extended dbContext use, such use should be used with caution/avoided where possible. See warning details : c-sharp-working-with-entity-framework-in-a-multi-threaded-server

Extend you DbContext with partial class Or add method to your existing extended partial class.

FYI - Please comment if still working on updated EntityFrameworkCore libraries.

public partial class FooDbContext : DbContext
{

    // Using Type: 5.0.16.0  EntityFrameworkCore.DbContext (confirm if working with any core library upgrades)
    
    public bool IsDisposed()
    {
        bool result = true;            
        var typeDbContext = typeof(DbContext);
        var isDisposedTypeField = typeDbContext.GetField("_disposed", BindingFlags.NonPublic | BindingFlags.Instance);

        if (isDisposedTypeField != null)
        {
            result = (bool)isDisposedTypeField.GetValue(this);
        }

        return result;
    }
}

Usage:

if (fooDbContext == null || fooDbContext.IsDisposed())
{
    // Recreate context
}

Peter O Brien
  • 105
  • 2
  • 6