Ошибка CA2000 с оператором using. Как сделать это действительным? - PullRequest
1 голос
/ 02 сентября 2010

Следующий код дает мне эту ошибку анализа кода

CA2000: Microsoft.Reliability: В методе 'SessionSummary.SessionSummary_Load (object, EventArgs)' вызовите System.IDisposable.Dispose для объекта "entity", прежде чем все ссылки на него выйдут из области видимости.

Я использую оператор using, поэтому удивляюсь:

    private void SessionSummary_Load(object sender, EventArgs e)
    {
        using (var entities = new DbEntities(Properties.Settings.Default.UserConnectionString))
        {
            entities.CommandTimeout = 7200;
            var sessions = from t in entities.TableName
                            where t.UserSession.Id == _id && t.Parent == 0
                            group t by new { t.UserSession, t.UserSession.SessionId } into sessionGroup
                            select new
                            {
                                Id = sessionGroup.Key.UserSession,                                   
                                Session = sessionGroup.Key.SessionId                                   
                            };

            summaryDataGridView.DataSource = sessions.Where(x => x.Time > 0.00);
            summaryDataGridView.Columns[4].DefaultCellStyle.Format = "N2";
            summaryDataGridView.Columns[4].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
        }
    }

Ответы [ 2 ]

4 голосов
/ 02 сентября 2010

У вас фактически есть преждевременное удаление, а не позднее, поскольку сущности, участвующие в замыкании, назначенном источнику данных, означают, что он покидает область действия.

Вы затем вывел сущности в дикую природу, которые не будут располагаться в своем реальном объеме, на что жалуется анализ, но вряд ли это будет проблемой по сравнению с тем фактом, что они удаляются до того, как они используются в последний раз.

Опции:

  1. Оставьте все как есть.Если вышеупомянутое работает, то это , вероятно , потому что удаление не влияет на необходимое состояние для закрытия.Это рискованно.Ставки на «вероятно» не очень хорошая идея, и они могут измениться в будущем.(Я могу вспомнить один случай, когда использование объекта после утилизации имеет смысл, но это неясно, а не то, что у вас здесь есть).

  2. Принудительно выполнить запрос с нетерпением.Вызов ToList() или ToArray() для запроса запустит его и создаст результат в памяти, который затем будет использоваться в качестве источника данных.В лучшем случае это будет менее эффективно как в пространстве, так и во времени.В худшем случае это может быть ужасно (в зависимости от размера результатов, с которыми вы имеете дело).

  3. Убедитесь, что элемент управления завершает использование своего источника данных, прежде чем покинуть область.Затем очистите источник данных.В зависимости от рассматриваемого элемента управления и некоторых других вопросов (в частности, если у него есть явный метод DataBind()), это может быть тривиально, невозможно или где-то посередине сделать это.

  4. Поместите сущность в переменную экземпляра.Реализация IDisposable.В вашем методе Dispose() вызовите его Dispose().Не добавляйте финализатор для этого, поскольку вы только удаляете управляемый объект.

  5. Создайте перечислимый метод, который переносит запрос (и использование), а затем выполняет yield return длякаждый элемент, который возвращает запрос.Используйте это как источник данных.

5 кажется лучшим выбором для большинства случаев.Он имеет преимущество в том, что не сильно меняет код, не добавляя (потенциально большие, в зависимости от данных) служебные данные числа 2. Обратите внимание, что просто вызов AsEnumerable (который имеет почти , тот же эффект влияет на порядок выполнения).) не будет иметь тот же эффект, так как закрытие все равно оставит блок неисполненным.

Редактировать: перечисляемый, который оборачивает запрос, будет выглядеть так:

private IEnumerable GetSessions()
{
    using (var entities = new DbEntities(Properties.Settings.Default.UserConnectionString))
    {
        entities.CommandTimeout = 7200;
        var sessions = from t in entities.TableName
                        where t.UserSession.Id == _id && t.Parent == 0
                        group t by new { t.UserSession, t.UserSession.SessionId } into sessionGroup
                        select new
                        {
                            Id = sessionGroup.Key.UserSession,                                   
                            Session = sessionGroup.Key.SessionId                                   
                        };

        foreach(var sess in sessions.Where(x => x.Time > 0.00))
          yield return sess;
    }
}

Тогда вы должны установитьизмените SessionSummary_Load на:

private void SessionSummary_Load(object sender, EventArgs e)
{
        summaryDataGridView.DataSource = GetSessions();
        summaryDataGridView.Columns[4].DefaultCellStyle.Format = "N2";
        summaryDataGridView.Columns[4].DefaultCellStyle.Alignment = DataGridViewContentAlignment.MiddleRight;
    }
}

Надеюсь, это решит проблему, потому что entities никогда не покидает область действия using.

3 голосов
/ 02 сентября 2010

Вы выполняете запрос в стиле LINQ для entities , но фактически не перечисляете результат в блоке using. Это создает проблему закрытия, поскольку информация запроса, хранящаяся в sessions.Where(x => x.Time > 0.00), сохраняется в summaryDataGridView.DataSource, и, таким образом, после выхода из кода, приведенного выше, в памяти остается ссылка на entities, которая все еще находится в памяти.

Объяснение этому заключается в том, что методы LINQ обеспечивают отложенное выполнение *, что означает, что ни session, ни значение, которое вы присваиваете summaryDataGridView.DataSource, не будут оцениваться в приведенном выше коде.

Чтобы форсировать оценку, вы должны быть в состоянии сделать это:

summaryDataGridView.DataSource = sessions.Where(x => x.Time > 0.00).ToList();

Добавление ToList() выше фактически приведет к выполнению вашего запроса и кешированию результатов в памяти. Кроме того, entities выйдет из области видимости, и у вас больше не будет ссылок на него через закрытие; поэтому я верю , что должно помочь вам.

* Примечание: это была только первая ссылка, которую я нашел в поиске Google. Это выглядело довольно хорошо, хотя.

...