以下代码给出了此代码分析错误
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;
}
}
答案 0 :(得分:4)
你实际上有一个潜在的过早处置,而不是一个迟到的处理,因为参与分配给数据源的闭包的实体意味着它离开了范围。
你已经然后在野外获得了一个不会被置于实际范围内的实体,这就是分析所抱怨的,但与此相比,这不太可能是一个问题。它是在最后一次使用之前处理的事实。
选项:
保持原样。如果上述情况有效则可能,因为处理不会影响闭包工作的必要状态。这很冒险。投注“可能”不是一个好主意,也可能改变道路。 (我可以想到一种情况,在处理之后使用一个物体是有道理的,但它是模糊的而不是你在这里所拥有的东西)。
强制执行查询。在查询上调用ToList()
或ToArray()
将运行它并创建一个内存中的结果,然后将其用作数据源。充其量虽然这在空间和时间上效率都会降低。更糟糕的是,它可能是瘫痪的(取决于你正在处理的结果的大小)。
在保留范围之前,确保控件使用其数据源完成。然后清除数据源。取决于所讨论的控件和其他一些事项(特别是,如果它有一个明确的DataBind()
方法),它可能是微不足道的,不可能的,或介于两者之间。
将实体放入实例变量中。实施IDisposable
。在Dispose()
方法中,将其称为Dispose()
方法。不要为此添加终结器,因为您只处理托管对象。
创建一个包装查询(和使用)的可枚举方法,然后对查询返回的每个项执行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
的范围。
答案 1 :(得分:3)
您正在entities
上执行LINQ样式的查询,但实际上并未枚举using
块中的结果。这会产生一个闭包问题,因为sessions.Where(x => x.Time > 0.00)
中存储的查询信息存储在summaryDataGridView.DataSource
中,因此在您的代码退出后,仍然会在内存中引用entities
。
对此的解释是LINQ方法提供deferred execution *,这意味着session
和summaryDataGridView.DataSource
分配的值都不会在上面的代码中进行评估。< / p>
要强制进行评估,您应该可以这样做:
summaryDataGridView.DataSource = sessions.Where(x => x.Time > 0.00).ToList();
上面添加ToList()
实际上会导致执行查询并将结果缓存在内存中。此外,entities
将超出范围,您将不再通过任何关闭引用它;所以我相信应该为你做的伎俩。
*注意:这只是我在Google搜索中找到的第一个链接。不过,它看起来还不错。