Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

"An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point" and "An unhandled exception was thrown by the application. System.InvalidOperationException: The connection was not closed. The connection's current state is connecting." #6488

Closed
ravipunjwani opened this issue Sep 8, 2016 · 13 comments

Comments

@ravipunjwani
Copy link

Steps to reproduce

Nothing different in my database configuration. I have configured my database as:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication();

            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);

            services.AddMvc(opts =>
            {
                opts.Filters.Add(new CustomJSONExceptionFilter());
            });

            services.AddDbContext<DbContext1>(options => options.UseSqlServer(Configuration.GetConnectionString("DbContext1")));
            services.AddDbContext<DbContext2>(options => options.UseSqlServer(Configuration.GetConnectionString("DbContext2")));
        }

The issue

As soon as I start the application, the log files show some errors referring to the database access.
The exception details are given as below:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HKUNESV0QS7N": An unhandled exception was thrown by the application.
System.InvalidOperationException: An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point.
   at Microsoft.EntityFrameworkCore.DbContext.InitializeServices()
   at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
   at Microsoft.EntityFrameworkCore.Infrastructure.AccessorExtensions.GetService[TService](IInfrastructure`1 accessor)
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.<.ctor>b__2_0()
   at Microsoft.EntityFrameworkCore.Internal.LazyRef`1.get_Value()
   at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.System.Linq.IQueryable.get_Provider()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ExecuteAsync[TSource,TResult](MethodInfo operatorMethodInfo, IQueryable`1 source, LambdaExpression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.SingleOrDefaultAsync[TSource](IQueryable`1 source, Expression`1 predicate, CancellationToken cancellationToken)
   at MyCompany.Services.Core.Services.OneService.<GetAsync>d__2.MoveNext() in C:\Projects\Source\Repos\MyCompany\Code\Projects\MyCompany.Services.Core\Services\OneService.cs:line 18

Further technical details

EF Core version: (1.0.0)
Operating system: Windows Server 2012 R2
Visual Studio version: VS 2015 update 3

This error comes up first few times the database is accessed in the code, but then this doesn't show up. However, another similar error often comes up when I make several requests on server simultaneously, like load test on several-threads. That error details is given below:

Exception Trace:

fail: Microsoft.AspNetCore.Server.Kestrel[13]
      Connection id "0HKUNESV0QSA4": An unhandled exception was thrown by the application.
System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.
   at System.Data.ProviderBase.DbConnectionClosedConnecting.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.OpenAsync(CancellationToken cancellationToken)
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Storage.RelationalConnection.<OpenAsync>d__31.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncQueryingEnumerable.AsyncEnumerator.<MoveNext>d__8.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.<_SingleOrDefault>d__123`1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Query.Internal.TaskResultAsyncEnumerable`1.Enumerator.<MoveNext>d__3.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at Microsoft.EntityFrameworkCore.Query.Internal.AsyncLinqOperatorProvider.ExceptionInterceptor`1.EnumeratorExceptionInterceptor.<MoveNext>d__5.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at MyCompany.Services.Core.Services.OneService.<GetAsync>d__2.MoveNext() in C:\Projects\Source\Repos\MyCompany\Code\Projects\MyCompany.Services.Core\Services\OneService.cs:line 18

@ajcvickers
Copy link
Member

@ravipunjwani This is most likely happening because the same context instance is being used concurrently by multiple threads. DbContext is not thread-safe. If the context instance is not being used concurrently but this is still happening, then please post some code showing how you are obtaining and using instances of the context.

@ravipunjwani
Copy link
Author

@ajcvickers I'm using the same context in several classes passing it through constructors. But, each of my methods in all these classes (that use the dbcontexts) are async. So ideally each call in the hierarchy would not get called unless the other calls get completed. Otherwise if they are still called simultaneously, definitely they would belong to separate http requests. The load-test I referred in the description were initiated using separate http-requests (and separate httpclients) for each call, from a single unit-test function.

@ravipunjwani
Copy link
Author

Below is the code from ServiceHelper class and startup.cs, and you can see I called the erring function serviceHelper.OneService.GetAsync in this place.

namespace MyCompany.Services.Core
{
    public class ServiceHelper
    {
        private DbContext1 dbContext1;
        private DbContext2 dbContext2;

        public SiteService SiteService;
        public OneService OneService;
        public NameService NameService;

        public ServiceHelper(DbContext1 dbContext1, DbContext2 dbContext2)
        {
            this.dbContext1 = dbContext1;
            this.dbContext2 = dbContext2;

            this.SiteService = new SiteService(dbContext1);
            this.OneService = new OneService(dbContext2);
            this.NameService = new NameService(dbContext1);
        }
    }
}


Startup.cs:
----------
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddAuthentication();

            // Add framework services.
            services.AddApplicationInsightsTelemetry(Configuration);

            services.AddMvc(opts =>
            {
                opts.Filters.Add(new CustomJSONExceptionFilter());
            });

            services.AddDbContext<DbContext1>(options => options.UseSqlServer(Configuration.GetConnectionString("DbContext1")));
            services.AddDbContext<DbContext2>(options => options.UseSqlServer(Configuration.GetConnectionString("DbContext2")));

            services.AddScoped<ServiceHelper>();
    }

        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IOptions<ConfigSettings> configSettings, ServiceHelper serviceHelper)
    {
            app.UseBasicAuthentication(new BasicAuthenticationOptions
            {
                Realm = "xxxx",
                AutomaticChallenge = false,
                Events = new BasicAuthenticationEvents
                {
                    OnValidateCredentials = async context =>
                    {
                        int siteId;
                        if (!int.TryParse(context.Username, out siteId))
                        {
                            return;
                        }

                        string key = context.Password;

                        vSite site = await serviceHelper.OneService.GetAsync(siteId);

                        if (site.key != key || site.SiteActive != 1)
                        {
                            return;
                        }

                        if (/*SOME-LOGIC-HERE*/)
                        {
                            ClaimsIdentity identity = new ClaimsIdentity(
                                new List<Claim>
                                {
                                                /* ADD-CLAIMS-HERE */
                                }
                            );

                            context.Ticket = new AuthenticationTicket(
                                new ClaimsPrincipal(identity),
                                new AuthenticationProperties(),
                                context.Options.AuthenticationScheme);
                        }

                        return;
                    }
                }
            });

            app.UseMvc();
    }

@ravipunjwani
Copy link
Author

Got this solved by myself.

I was incorrectly using my serviceHelper object (can be seen in Configure function parameters injected as service).

Removed this parameter and replaced the line:
vSite site = await serviceHelper.OneService.GetAsync(siteId);
with

                        ServiceHelper serviceHelper = context.HttpContext.RequestServices.GetService<ServiceHelper>();
                        vSite site = await serviceHelper.OneService.GetAsync(siteId);

This gets the ServiceHelper instance from the current request context, instead of the one passed in Configure function as injected service.

@snaveevans
Copy link

@ajcvickers Hey I'm getting the same error message. Mine has something to do with concurrency issues although I'm not sure why... My app is stateless and basically is an api. I set up my context with
services.AddDbContext<AvocadoContext>(options => options.UseNpgsql(connectionString));
and is accessed in middleware through a service
services.AddTransient<EventMemberService>();
everything is fine as long as I only perform one request at a time. If I perform multiple requests however I get
An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point
Entity Framework can handle this right? I thought EF was fine as long as a new Instance of the Context was created on each request, which is the default ServiceLifetime (Scoped). If so any ideas on why I'm getting this error? Maybe I have some bad configuration... if so though I'm not sure where to look. Any help would be greatly appreciated. Thanks!

@ajcvickers
Copy link
Member

@snaveevans Have you verified that you are getting a new instance each time?

@ravipunjwani
Copy link
Author

Hi @snaveevans ,

EventMemberService eventMemberService = context.HttpContext.RequestServices.GetService<EventMemberService>()

The above line of code might fix your problem, as it did for me the same way.

Though I added my service via services.AddScoped compared to yours services.AddTransient.
Both should work, however I wanted to avoid creating another instance within same request. Visit here for more on the difference between transient and scoped.

@snaveevans
Copy link

snaveevans commented Dec 6, 2016

@ajcvickers not sure how to check if it is creating a new instance or not, how can I do that? (for future reference) It's now fixed though.
@ravipunjwani Thanks! that code worked for me as well. I switched them all to Scoped instead of Transient and I still get the same issue. But once I pull the service directly from the HttpContext it runs just fine... Why is that though? I used the "standard" way of adding services via constructor injection.
Edit:
I would have thought that getting the service through the constructor or the HttpContext would end up with the same service instance. I'm guessing it doesn't though.

@ajcvickers
Copy link
Member

@snaveevans Typical ways are using the debugger or tagging each instance with a unique value and logging it.

@ravipunjwani
Copy link
Author

@snaveevans The same SO issue that I referred to earlier, shows exactly how to achieve this and see differences visually.

Switching transient and scoped would be same for you because you were using constructor injection, which would work great for the Configure method in Startup.cs. The middleware is placed in the request pipeline and would need a reference to the service instance from current request context (here comes the decision of transient and scoped - transient would give you new instance even in current request and scoped would give you a unique instance per http request lifecycle).

I also got confused earlier but figured out sooner than later as the production app started creating a lot of issues.

@nshathish
Copy link

I am having the same issue as @snaveevans described. My app is also a webapi, and console application calls this api endpoint everytime a file is dropped into a hotfolder. The web api doesnt throw any errors if I call the endpoint in a sequential manner. As soon as start calling it in async method, the api end point throws this error:
An attempt was made to use the context while it is being configured. A DbContext instance cannot be used inside OnConfiguring since it is still being configured at this point

Any help is really appreciated
Thanks

@ajcvickers
Copy link
Member

@nshathish The context is not thread safe. From what you say it seems likely that their are multiple calls accessing the same context instance, possibly due to how your async code is written.

@joydeeprony89
Copy link

@nshathish In your WebApi project the DbContext injection has to be Transient or PerThread.

@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants