Properly handling DbContexts in ASP.NET Core WebApi
<p>Got a small confusion here.
I'm not sure if I am handling my DbContext throughout the WebApi properly.
I do have some controllers that do some operations on my DB (Inserts/Updates with EF) and after doing these actions I do trigger an event.
In my EventArgs (I have a custom class which inherits from <code>EventArgs</code>) I pass my <code>DbContext</code> and I use it in the event handler to log these operations (basically I just log authenticated user API requests).</p>

<p>In the event handler when I am trying to commit my changes (<code>await SaveChangesAsync</code>) I get an error : "Using a disposed object...etc" basically noticing me that at the first time I use <code>await</code> in my <code>async void</code> (fire and forget) I notify the caller to dispose the Dbcontext object.</p>

<p>Not using <code>async</code> works and the only workaround that I've mangaged to put out is by creating another instance of DbContext by getting the SQLConnectionString of the EventArgs passed DbContext.</p>

<p>Before posting I did made a small research based on my issue
<a href="https://stackoverflow.com/questions/35988090/entity-framework-disposing-with-async-controllers-in-web-api-mvc">Entity Framework disposing with async controllers in Web api/MVC</a></p>

<p>This is how I pass parameters to my <code>OnRequestCompletedEvent</code></p>

<pre><code>OnRequestCompleted(dbContext: dbContext,requestJson: JsonConvert.SerializeObject);
</code></pre>

<p>This is the <code>OnRequestCompleted()</code> declaration</p>

<pre><code> protected virtual void OnRequestCompleted(int typeOfQuery,PartnerFiscalNumberContext dbContext,string requestJson,string appId)
        {
       RequestCompleted?.Invoke(this,new MiningResultEventArgs()
          {
            TypeOfQuery = typeOfQuery,
            DbContext   = dbContext,
            RequestJson = requestJson,
            AppId = appId
          });
        }
</code></pre>

<p>And this is how I process and use my <code>dbContext</code></p>

<pre><code>var appId = miningResultEventArgs.AppId;
var requestJson = miningResultEventArgs.RequestJson;
var typeOfQuery = miningResultEventArgs.TypeOfQuery;
var requestType =  miningResultEventArgs.DbContext.RequestType.FirstAsync(x =&gt; x.Id == typeOfQuery).Result;
var apiUserRequester =  miningResultEventArgs.DbContext.ApiUsers.FirstAsync(x =&gt; x.AppId == appId).Result;

var apiRequest = new ApiUserRequest()
{
    ApiUser = apiUserRequester,
    RequestJson = requestJson,
    RequestType = requestType
};

miningResultEventArgs.DbContext.ApiUserRequests.Add(apiRequest);
await miningResultEventArgs.DbContext.SaveChangesAsync();
</code></pre>

<p>By using <code>SaveChanges</code> instead of <code>SaveChangesAsync</code> everything works.
My only idea is to create another dbContext by passing the previous DbContext's SQL connection string </p>

<pre><code>var dbOptions = new DbContextOptionsBuilder&lt;PartnerFiscalNumberContext&gt;();
dbOptions.UseSqlServer(miningResultEventArgs.DbContext.Database.GetDbConnection().ConnectionString);

    using (var dbContext = new PartnerFiscalNumberContext(dbOptions.Options))
    {
        var appId = miningResultEventArgs.AppId;
        var requestJson = miningResultEventArgs.RequestJson;
        var typeOfQuery = miningResultEventArgs.TypeOfQuery;


        var requestType = await dbContext.RequestType.FirstAsync(x =&gt; x.Id == typeOfQuery);
        var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x =&gt; x.AppId == appId);

        var apiRequest = new ApiUserRequest()
        {
            ApiUser = apiUserRequester,
            RequestJson = requestJson,
            RequestType = requestType
        };

        dbContext.ApiUserRequests.Add(apiRequest);
        await dbContext.SaveChangesAsync();
    }
</code></pre>

<p>The latter code excerpt is just a small test to check my supposition, basically I should pass the SQL connection string instead of the DbContext object.</p>

<p>I am not sure (in terms of best practice) if I should pass a connection string and create a new dbContext object (and dispose it by using a <code>using</code> clause) or if I should use/have another mindset for this issue.</p>

<p>From what I know, using a DbContext should be done for a limited set of operations and not for multiple purposes.</p>

<p><strong>EDIT 01</strong> </p>

<p>I'm going to detail more thorough what I've been doing down below.</p>

<p>I think I got an idea of why this error happens.</p>

<p>I have 2 controllers
One that receives a JSON and after de-serializing it I return a JSON to the caller and another controller that gets a JSON that encapsulates a list of objects that I iterate in an async way, returning an <code>Ok()</code> status.</p>

<p>The controllers are declared as <code>async Task&lt;IActionResult&gt;</code> and both feature an <code>async</code> execution of 2 similar methods.</p>

<p>The first one that returns a JSON executes this method</p>

<pre><code>await ProcessFiscalNo(requestFiscalView.FiscalNo, dbContext);
</code></pre>

<p>The second one (the one that triggers this error)</p>

<pre><code>foreach (string t in requestFiscalBulkView.FiscalNoList)
       await ProcessFiscalNo(t, dbContext);
</code></pre>

<p>Both methods (the ones defined previously) start an event <code>OnOperationComplete()</code>
Within that method I execute the code from my post's beginning.
Within the <code>ProcessFiscalNo</code> method I DO NOT use any using contexts nor do I dispose the dbContext variable.
Within this method I only commit 2 major actions either updating an existing sql row or inserting it.
For edit contexts I select the row and tag the row with the modified label by doing this</p>

<pre><code>dbContext.Entry(partnerFiscalNumber).State = EntityState.Modified;
</code></pre>

<p>or by inserting the row</p>

<pre><code>dbContext.FiscalNumbers.Add(partnerFiscalNumber);
</code></pre>

<p>and finally I execute an <code>await dbContext.SaveChangesAsync();</code></p>

<p>The error always gets triggered within the EventHandler ( the one detailed @ the beginning of the thread) during the <code>await dbContext.SaveChangedAsync()</code>
which is pretty weird since 2 lines before that I do await reads on my DB with EF.</p>

<pre><code> var requestType = await dbContext.RequestType.FirstAsync(x =&gt; x.Id == typeOfQuery);
 var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x =&gt; x.AppId == appId);

 dbContext.ApiUserRequests.Add(new ApiUserRequest() { ApiUser = apiUserRequester, RequestJson = requestJson, RequestType = requestType });

  //this throws the error
 await dbContext.SaveChangesAsync();
</code></pre>

<p>For some reason calling await within the Event Handler notifies the caller to dispose the <code>DbContext</code> object.
Also by re-creating the <code>DbContext</code> and not re-using the old one I see a huge improvement on access.
Somehow when I use the first controller and return the info the <code>DbContext</code> object appears to get flagged by the CLR for disposal but for some unknown reason it still functions.</p>

<p><strong>EDIT 02</strong>
Sorry for the bulk-ish content that follows, but I've placed all of the areas where I do use dbContext.</p>

<p>This is how I'm propagating my dbContext to all my controllers that request it.</p>

<pre><code> public void ConfigureServices(IServiceCollection services)
        {

         // Add framework services.
        services.AddMemoryCache();

        // Add framework services.
        services.AddOptions();
        var connection = @"Server=.;Database=CrawlerSbDb;Trusted_Connection=True;";
        services.AddDbContext&lt;PartnerFiscalNumberContext&gt;(options =&gt; options.UseSqlServer(connection));

        services.AddMvc();
        services.AddAuthorization(options =&gt;
        {
            options.AddPolicy("PowerUser",
                              policy =&gt; policy.Requirements.Add(new UserRequirement(isPowerUser: true)));
        });

        services.TryAddSingleton&lt;IHttpContextAccessor, HttpContextAccessor&gt;();
        services.AddSingleton&lt;IAuthorizationHandler, UserTypeHandler&gt;();
    }
</code></pre>

<p>In Configure I'm using the dbContext for my custom MiddleWare</p>

<pre><code> public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            var context = app.ApplicationServices.GetService&lt;PartnerFiscalNumberContext&gt;();
            app.UseHmacAuthentication(new HmacOptions(),context);

            app.UseMvc();
        }
</code></pre>

<p>In the custom MiddleWare I'm only using it for a query.</p>

<pre><code>public HmacHandler(IHttpContextAccessor httpContextAccessor, IMemoryCache memoryCache, PartnerFiscalNumberContext partnerFiscalNumberContext)
        {
            _httpContextAccessor = httpContextAccessor;
            _memoryCache = memoryCache;
            _partnerFiscalNumberContext = partnerFiscalNumberContext;

            AllowedApps.AddRange(
                    _partnerFiscalNumberContext.ApiUsers
                        .Where(x =&gt; x.Blocked == false)
                        .Where(x =&gt; !AllowedApps.ContainsKey(x.AppId))
                        .Select(x =&gt; new KeyValuePair&lt;string, string&gt;(x.AppId, x.ApiHash)));
        }
</code></pre>

<p>In my controller's CTOR I'm passing the dbContext</p>

<pre><code>public FiscalNumberController(PartnerFiscalNumberContext partnerContext)
        {
            _partnerContext = partnerContext;
        }
</code></pre>

<p>This is my Post</p>

<pre><code>        [HttpPost]
        [Produces("application/json", Type = typeof(PartnerFiscalNumber))]
        [Consumes("application/json")]
        public async Task&lt;IActionResult&gt; Post([FromBody]RequestFiscalView value)
        {
            if (!ModelState.IsValid)
                return BadRequest(ModelState);

            var partnerFiscalNo = await _fiscalNoProcessor.ProcessFiscalNoSingle(value, _partnerContext);
        }
</code></pre>

<p>Within the <code>ProcessFiscalNoSingle</code> method I have the following usage, If that partner exists then I'll grab him, if not, create and return him.</p>

<pre><code>internal async Task&lt;PartnerFiscalNumber&gt; ProcessFiscalNoSingle(RequestFiscalView requestFiscalView, PartnerFiscalNumberContext dbContext)
        {
            var queriedFiscalNumber =  await dbContext.FiscalNumbers.FirstOrDefaultAsync(x =&gt; x.FiscalNo == requestFiscalView.FiscalNo &amp;&amp; requestFiscalView.ForceRefresh == false) ??
                                       await ProcessFiscalNo(requestFiscalView.FiscalNo, dbContext, TypeOfQuery.Single);

            OnRequestCompleted(typeOfQuery: (int)TypeOfQuery.Single, dbContextConnString: dbContext.Database.GetDbConnection().ConnectionString, requestJson: JsonConvert.SerializeObject(requestFiscalView), appId: requestFiscalView.RequesterAppId);

            return queriedFiscalNumber;
        }
</code></pre>

<p>Further down in the code, there's the <code>ProcessFiscalNo</code> method where I use the dbContext</p>

<pre><code> var existingItem =
        dbContext.FiscalNumbers.FirstOrDefault(x =&gt; x.FiscalNo == partnerFiscalNumber.FiscalNo);

    if (existingItem != null)
    {
        var existingGuid = existingItem.Id;
        partnerFiscalNumber = existingItem;

        partnerFiscalNumber.Id = existingGuid;
        partnerFiscalNumber.ChangeDate = DateTime.Now;

        dbContext.Entry(partnerFiscalNumber).State = EntityState.Modified;
    }
    else
        dbContext.FiscalNumbers.Add(partnerFiscalNumber);

    //this gets always executed at the end of this method
    await dbContext.SaveChangesAsync();
</code></pre>

<p>Also I've got an Event called OnRequestCompleted() where I pass my actual dbContext (after it ends up with SaveChangesAsync() if I update/create it)</p>

<p>The way I initiate the event args.</p>

<pre><code> RequestCompleted?.Invoke(this, new MiningResultEventArgs()
            {
                TypeOfQuery = typeOfQuery,
                DbContextConnStr = dbContextConnString,
                RequestJson = requestJson,
                AppId = appId
            });
</code></pre>

<p>This is the notifier class (where the error occurs)</p>

<pre><code>internal class RequestNotifier : ISbMineCompletionNotify
    {
        public async void UploadRequestStatus(object source, MiningResultEventArgs miningResultArgs)
        {
            await RequestUploader(miningResultArgs);
        }

        /// &lt;summary&gt;
        /// API Request Results to DB
        /// &lt;/summary&gt;
        /// &lt;param name="miningResultEventArgs"&gt;EventArgs type of a class that contains requester info (check MiningResultEventArgs class)&lt;/param&gt;
        /// &lt;returns&gt;&lt;/returns&gt;
        private async Task RequestUploader(MiningResultEventArgs miningResultEventArgs)
        {
            //ToDo - fix the following bug : Not being able to re-use the initial DbContext (that's being used in the pipeline middleware and controller area), 
            //ToDo - basically I am forced by the bug to re-create the DbContext object

            var dbOptions = new DbContextOptionsBuilder&lt;PartnerFiscalNumberContext&gt;();
            dbOptions.UseSqlServer(miningResultEventArgs.DbContextConnStr);

            using (var dbContext = new PartnerFiscalNumberContext(dbOptions.Options))
            {
                var appId = miningResultEventArgs.AppId;
                var requestJson = miningResultEventArgs.RequestJson;
                var typeOfQuery = miningResultEventArgs.TypeOfQuery;

                var requestType = await dbContext.RequestType.FirstAsync(x =&gt; x.Id == typeOfQuery);
                var apiUserRequester = await dbContext.ApiUsers.FirstAsync(x =&gt; x.AppId == appId);

                var apiRequest = new ApiUserRequest()
                {
                    ApiUser = apiUserRequester,
                    RequestJson = requestJson,
                    RequestType = requestType
                };

                dbContext.ApiUserRequests.Add(apiRequest);
                await dbContext.SaveChangesAsync();
            }
        }
    }
</code></pre>

<p>Somehow when the dbContext reaches the Event Handler CLR gets notified to dispose the dbContext object (because I'm using await?)
Without recreating the object I was having huge lag when I wanted to use it.</p>

<p>While writing this I have an idea, I did upgrade my solution to 1.1.0 and I'm gonna try to see if it behaves similarly.</p>