Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Occasional "The underlying provider failed on Open" errors when using EF4 (edmx model)

I hope someone can help me with a solution to the following error. The application in which the error happens is running in production and I never experience the error myself. However around 20 times a day I receive an error mail telling me:

The underlying provider failed on Open. ---> System.InvalidOperationException: The connection was not closed. The connection's current state is connecting.

Here's the stack trace

System.Data.EntityException: The underlying provider failed on Open. ---> System.InvalidOperationException: The connection was not closed. The connection's current state is connecting. at System.Data.ProviderBase.DbConnectionBusy.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) at System.Data.SqlClient.SqlConnection.Open() at HibernatingRhinos.Profiler.Appender.ProfiledDataAccess.ProfiledConnection.Open() at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure) --- End of inner exception stack trace --- at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure) at System.Data.EntityClient.EntityConnection.Open() at System.Data.Objects.ObjectContext.EnsureConnection() at System.Data.Objects.ObjectQuery1.GetResults(Nullable1 forMergeOption) at System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1 source) at System.Data.Objects.ELinq.ObjectQueryProvider.b__1[TResult](IEnumerable1 sequence) at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable1 query, Expression queryRoot) at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression) at System.Linq.Queryable.FirstOrDefault[TSource](IQueryable`1 source)
at GuideSites.DomainModel.Repositories.ClinicTermRepository.GetClinicTermByGuideSiteId(Int32 guideSiteId) in C:\Projects\GuideSites\GuideSites.DomainModel\Repositories\ClinicTermRepository.cs:line 20 at GuideSites.Web.Frontend.Helpers.VerifyUrlHelper.RedirectOldUrls() in C:\Projects\GuideSites\GuideSites.Web.Frontend\Helpers\VerifyUrlHelper.cs:line 91 at GuideSites.Web.Frontend.MvcApplication.Application_BeginRequest(Object sender, EventArgs e) in C:\Projects\GuideSites\GuideSites.Web.Frontend\Global.asax.cs:line 412 at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)

I use EF4 through an EDMX model and I the way I connect to the database (MS SQL 2008) is through an HttpContext-based per-request object context so that connections to the database aren't opened and closed for every single piece of data I need on a given pageload.

My Database context class looks like this:

public class DatabaseContext : IDisposable
{
    private const string ContextName = "context";
    private static dbEntities _dbEntities;

    public dbEntities GetDatabaseContext()
    {
        SqlConnection.ClearAllPools();

        if (HttpContext.Current == null)
            return _dbEntities ?? (_dbEntities = new dbEntities());

        if (HttpContext.Current.Items[ContextName] == null)
            HttpContext.Current.Items[ContextName] = new dbEntities();

        _dbEntities = (dbEntities)HttpContext.Current.Items[ContextName];
        if (_dbEntities.Connection.State == ConnectionState.Closed)
        {
            _dbEntities.Connection.Open();
            return _dbEntities;
        }

        return _dbEntities;
    }


    public void RemoveContext()
    {
        if (HttpContext.Current != null && HttpContext.Current.Items[ContextName] != null)
        {
            ((dbEntities)HttpContext.Current.Items[ContextName]).Dispose();
            HttpContext.Current.Items[ContextName] = null;
        }

        if (_dbEntities != null)
        {
            _dbEntities.Dispose();
            _dbEntities = null;
        }
    }


    public void Dispose()
    {
        RemoveContext();
    }

}

In my repositories I use the database context like this:

public class SomeRepository
{
    private static readonly object Lock = new object();
    private readonly dbEntities _dbEntities;

    public SomeRepository()
    {
        var databaseContext = new DatabaseContext();
        _dbEntities = databaseContext.GetDatabaseContext();
    }


    public IEnumerable<SomeRecord> GetSomeData(int id)
    {
        lock (Lock)
        {
            return
                _dbEntities.SomeData.Where(c => c.Id == id);
        }
    }
 }

The lock(Lock) thing was something I read about should help this problem but in my case it hasn't. And generally it's been difficult finding threads that describe exactly my problem let alone a solution to the problem.

The application is an ASP.NET MVC3 application and it's setup as one application running for 9 different websites (the domain determines the content to be served to the client). The 9 websites doesn't have more than 2.000 pageviews daily, so the database should be stressed on that account.

I hope someone can help and please let me know if there is something I forgot to mention.

like image 530
hylle Avatar asked Jan 30 '12 19:01

hylle


People also ask

What does the underlying provider failed to open mean?

Probable cause of the issue could be as below: The user instance cannot attach the database because the user does not have the required permissions. The user instance executes in the context of the user who opened the connection—not the normal SQL Server service account.


1 Answers

As per my comment, Dispose() has to be called by something at the end of the request. You can do this with an HttpModule like so:

public class ContextDisposer : IHttpModule
{
    private readonly DatabaseContext _context = new DatabaseContext();

    public void Init(HttpApplication context)
    {
        context.EndRequest += (sender, e) => this.DisposeContext(sender, e);
    }

    private static bool DoesRequestCompletionRequireDisposing(
        string requestPath)
    {
        string fileExtension = Path.GetExtension(requestPath)
            .ToUpperInvariant();

        switch (fileExtension)
        {
            case ".ASPX":
            case string.Empty:
            case null:
                return true;
        }

        return false;
    }

    private void DisposeContext(object sender, EventArgs e)
    {
        // This gets fired for every request to the server, but there's no 
        // point trying to dispose anything if the request is for (e.g.) a 
        // gif, so only call Dispose() if necessary:
        string requestedFilePath = ((HttpApplication)sender).Request.FilePath;

        if (DoesRequestCompletionRequireDisposing(requestedFilePath))
        {
            this._context.Dispose();
        }
    }
}

You then plug the module into the request pipeline like this (you put it into system.web and system.webserver so it's included for IIS and the VS dev web server):

<system.web>
    <httpModules>
        <add name="ContextDisposer" 
             type="MyNamespace.ContextDisposer" />
    </httpModules>
</system.web>

<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="ContextDisposer" 
             type="MyNamespace.ContextDisposer" />
    </modules>
</system.webServer>
like image 132
Steve Wilkes Avatar answered Oct 26 '22 02:10

Steve Wilkes