Иногда Базовый провайдер отказывает при открытии ошибок при использовании EF4 (модель edmx)

Я надеюсь, что кто-то может помочь мне с решением следующей ошибки. Приложение, в котором возникает ошибка, работает в производственной среде, и я никогда не сталкивался с этой ошибкой. Однако примерно 20 раз в день я получаю сообщение об ошибке:

Базовый провайдер не удалось открыть. ---> System.InvalidOperationException: соединение не было закрыто. Текущее состояние подключения - подключение.

Вот трассировка стека

System.Data.EntityException: сбой базового поставщика при открытии. ---> System.InvalidOperationException: соединение не было закрыто. Текущее состояние подключения - подключение. в System.Data.ProviderBase.DbConnectionBusy.OpenConnection (DbConnection outerConnection, DbConnectionFactory connectionFactory) в System.Data.SqlClient.SqlConnection.Open () в HibernatingRhinos.Profiler.Appender.ProfiledDataAccess.Profiler.Connection.ProfiledDataAccess.Profiled.Connection. EntityConnection.OpenStoreConnectionIf (Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String TryptedOperation, Boolean & closeStoreConnectionOnFailure) --- Конец внутреннего стека исключений трассировки --- в System.Data.EntityConnectionConnectionOnFailureConnectionIntityConnectionIntityConnectionIntityConnectionIntityConnectionConnectionOpenityConnectionIntityConnectionOnectionConnectionOntityConnectionOnepenityConnectionOntityConnectionConnectionOntityConnectionData.EntityConnectionIntityConnectionOpenityConnectionIntityConnection , DbConnection originalConnection, String excludedOperation, Boolean & closeStoreConnectionOnFailure) в System.Data.EntityClient.EntityConnection.Open () в System.Data.Objects.ObjectContext.EnsureConnection () в System.Data.Objects._1ObjectQuery. forMergeOption) в источнике System.Data.Objects.ObjectQuery1.System.Collections.Generic.IEnumerable<T>.GetEnumerator() at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable1) в System.Data.Objects.ELinq.ObjectQueryProvider.b__1 [TResult] (запрос IEnumerable1 sequence) at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable1, Expression queryRoot) в System.Data.Objects.ELinq.ObjectSueryProvider. .Execute [S] (выражение выражения) в System.Linq.Queryable.FirstOrDefault [TSource] (источник IQueryable`1)
в GuideSites.DomainModel.Repositories.ClinicTermRepository.GetClinicTermByGuideSiteId (Int32) в GuideSiteId (Int32) \ GuideSites.DomainModel \ Repositories \ ClinicTermRepository.cs: строка 20 в GuideSites.Web.Frontend.Helpers.VerifyUrlHelper.RedirectOldUrls () в C: \ Projects \ GuideSites \ GuideSites.Web.Frontend \ Helpers \ VerifyUrlHelper: строка 91. GuideSites.Web.Frontend.MvcApplication.Application_BeginRequest (отправитель объекта, EventArgs e) в C: \ Projects \ GuideSites \ GuideSites.Web.Frontend \ Global.asax.cs: строка 412 в System.Web.HttpApplication.SyncEventExecutionStep.SyncEventExecutionStep.System. b.HttpApplication.IExecutionStep.Execute () в System.Web.HttpApplication.ExecuteStep (шаг IExecutionStep, логический и завершенный синхронно)

Я использую EF4 через модель EDMX, и способ подключения к базе данных (MS SQL 2008) - через контекст объекта запроса на основе HttpContext, так что подключения к базе данных не открываются и не закрываются для каждого отдельного фрагмента данных. Мне нужно на данной странице.

Класс контекста My Database выглядит так:

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();
    }

}

В своих репозиториях я использую контекст базы данных следующим образом:

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);
        }
    }
 }

Я читал о том, что блокировка (блокировка) должна помочь в этой проблеме, но в моем случае это не так. И вообще было сложно найти темы, которые точно описывают мою проблему, не говоря уже о ее решении.

Приложение представляет собой приложение ASP.NET MVC3 и настраивается как одно приложение, работающее для 9 разных веб-сайтов (домен определяет контент, который будет обслуживаться клиенту). У 9 веб-сайтов не более 2 000 просмотров страниц в день, поэтому на эту учетную запись следует обратить внимание на базу данных.

Я надеюсь, что кто-то может помочь, и, пожалуйста, дайте мне знать, если я что-то забыл упомянуть.


person hylle    schedule 30.01.2012    source источник
comment
Что вызывает DatabaseContext.Dispose()? Я использую аналогичную HttpContext.Items настройку и имею HttpModule, который удаляет ObjectContext в конце запроса ...   -  person Steve Wilkes    schedule 31.01.2012
comment
На самом деле я думал, что Dispose() вызывается автоматически, поскольку DatabaseContext реализует IDisposable. Но если это не так, это определенно может объяснить ошибку. Могу я взглянуть на ваш код в HttpModule?   -  person hylle    schedule 31.01.2012


Ответы (1)


Согласно моему комментарию, Dispose() должен вызываться чем-то в конце запроса. Вы можете сделать это с помощью HttpModule вот так:

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();
        }
    }
}

Затем вы вставляете модуль в конвейер запросов следующим образом (вы помещаете его в system.web и system.webserver, чтобы он был включен для IIS и веб-сервера VS dev):

<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>
person Steve Wilkes    schedule 31.01.2012
comment
Я нашел еще несколько примеров и реализовал их вчера вечером. По сути, это то же самое, что вы написали здесь, и вы определенно направили меня на правильный путь. Так что большое спасибо! - person hylle; 31.01.2012
comment
С прошлой ночи я получил всего несколько писем об ошибках, связанных с этой проблемой. Вот один из них: Новая транзакция не разрешена, потому что в сеансе работают другие потоки. И вот другой: Экземпляр ObjectContext удален и больше не может использоваться для операций, которые требуется соединение Последнее предполагает некоторую проблему с отложенной загрузкой, но поскольку я получил только один, я не думаю, что это слишком серьезно. - person hylle; 31.01.2012
comment
Я сам никогда не видел первое сообщение об ошибке, но второе, вероятно, вызвано контекстами объектов, которые зависают и пытаются использоваться в запросах; есть ли у вас какие-либо объекты EF, хранящиеся в статической области или в местах, где ссылка на них будет существовать дольше, чем запрос? - person Steve Wilkes; 31.01.2012
comment
Я веду журнал для endrequest с другим HttpModule, но он просто записывает строки журнала в плоские файлы, которые затем импортируются в базу данных позже. И только мои репозитории имеют доступ к базе данных, и ни один из репозиториев не имеет статических методов. Но больше этой ошибки я не видел, так как немцы говорят: Einmal ist kein mal (один раз - никогда) ;-) - person hylle; 01.02.2012