带有'using'语句的CA2000错误。如何使这个有效?

时间:2010-09-01 23:55:49

标签: c# code-analysis using

以下代码给出了此代码分析错误

  

CA2000:Microsoft.Reliability:在方法'SessionSummary.SessionSummary_Load(object,EventArgs)'中,在对所有对它的引用超出范围之前,在对象'entities'上调用System.IDisposable.Dispose。

我正在使用“使用”声明,因此我对此感到惊讶:

    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 个答案:

答案 0 :(得分:4)

你实际上有一个潜在的过早处置,而不是一个迟到的处理,因为参与分配给数据源的闭包的实体意味着它离开了范围。

你已经然后在野外获得了一个不会被置于实际范围内的实体,这就是分析所抱怨的,但与此相比,这不太可能是一个问题。它是在最后一次使用之前处理的事实。

选项:

  1. 保持原样。如果上述情况有效则可能,因为处理不会影响闭包工作的必要状态。这很冒险。投注“可能”不是一个好主意,也可能改变道路。 (我可以想到一种情况,在处理之后使用一个物体是有道理的,但它是模糊的而不是你在这里所拥有的东西)。

  2. 强制执行查询。在查询上调用ToList()ToArray()将运行它并创建一个内存中的结果,然后将其用作数据源。充其量虽然这在空间和时间上效率都会降低。更糟糕的是,它可能是瘫痪的(取决于你正在处理的结果的大小)。

  3. 在保留范围之前,确保控件使用其数据源完成。然后清除数据源。取决于所讨论的控件和其他一些事项(特别是,如果它有一个明确的DataBind()方法),它可能是微不足道的,不可能的,或介于两者之间。

  4. 将实体放入实例变量中。实施IDisposable。在Dispose()方法中,将其称为Dispose()方法。不要为此添加终结器,因为您只处理托管对象。

  5. 创建一个包装查询(和使用)的可枚举方法,然后对查询返回的每个项执行yield return。使用它作为数据源。

  6. 对于大多数情况来说,

    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的范围。

答案 1 :(得分:3)

您正在entities 上执行LINQ样式的查询,但实际上并未枚举using块中的结果。这会产生一个闭包问题,因为sessions.Where(x => x.Time > 0.00)中存储的查询信息存储在summaryDataGridView.DataSource中,因此在您的代码退出后,仍然会在内存中引用entities

对此的解释是LINQ方法提供deferred execution *,这意味着sessionsummaryDataGridView.DataSource分配的值都不会在上面的代码中进行评估。< / p>

要强制进行评估,您应该可以这样做:

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

上面添加ToList()实际上会导致执行查询并将结果缓存在内存中。此外,entities将超出范围,您将不再通过任何关闭引用它;所以我相信应该为你做的伎俩。

*注意:这只是我在Google搜索中找到的第一个链接。不过,它看起来还不错。