“使用”出现 CA2000 错误陈述。如何使这个有效?

发布于 2024-09-17 15:45:25 字数 1321 浏览 7 评论 0原文

以下代码给了我这个代码分析错误

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

我正在使用“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;
        }
    }

The following code is giving me this code analysis error

CA2000 : Microsoft.Reliability : In method 'SessionSummary.SessionSummary_Load(object, EventArgs)', call System.IDisposable.Dispose on object 'entities' before all references to it are out of scope.

I am using a 'using' statement, so I am surprised on this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

千秋岁 2024-09-24 15:46:15

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

对此的解释是 LINQ 方法提供 延迟执行*,这意味着上面的代码中不会评估session 和您分配给summaryDataGridView.DataSource 的值。

要强制执行计算,您应该能够执行以下操作:

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

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

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

You are performing a LINQ-style query over entities but are not actually enumerating over the result within the using block. This creates a closure issue, since the query information stored in sessions.Where(x => x.Time > 0.00) is being stored in summaryDataGridView.DataSource and there is thus a reference to entities still in memory after your code above exits.

The explanation for this is that LINQ methods provide deferred execution*, which means that neither session nor the value you're assigning to summaryDataGridView.DataSource will be evaluated in the code above.

To force evaluation, you should be able to do this:

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

The addition of ToList() above will actually cause your query to be executed and the results to be cached in memory. Furthermore, entities will have gone out of scope and you will have no more references to it via any closure; so I believe that should do the trick for you.

*Note: this was just the first link I found from a Google search. It looked pretty good, though.

风为裳 2024-09-24 15:46:02

实际上,您可能会过早处置,而不是较晚处置,因为参与分配给数据源的闭包的实体意味着它离开了范围。

然后得到了一个在野外的实体,它不会在其真实范围内被处置,这正是分析所抱怨的,但与以下事实相比,这不太可能成为问题:它在最后一次使用之前被丢弃。

选项:

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

You've actually got a potentially premature disposal, rather than a late one, as entities being involved in the closure assigned to the datasource means it leaves the scope.

You've then got an entities out in the wild that won't be disposed in its real scope, which is what the analysis is complaining about, but that's unlikely to be a problem compared with the fact that it is disposed before it is last used.

Options:

  1. Leave it as it is. If the above works then it is probably because disposal doesn't affect the necessary state for the closure to work. This is dicey. Betting on "probably" isn't a good idea, and may also change down the road. (I can think of one case where using an object after disposal makes sense, but it's obscure and not what you have here anyway).

  2. Force the query to execute eagerly. Calling ToList() or ToArray() on the query will run it and create an in-memory result that is then used as the datasource. At best though this will be less efficient in both space and time. At worse it could be cripplingly so (depending on the size of results you are dealing with).

  3. Make sure the control completes using its datasource before the scope is left. Then clear the datasource. Depending on the control in question and some other matters (in particular, if it has an explicit DataBind() method) it can be trivial, impossible or somewhere in between to do this.

  4. Put entity into an instance variable. Implement IDisposable. In your Dispose() method, call it's Dispose() method. Do not add a finaliser for this, as you are only disposing a managed object.

  5. Create an enumerable method that wraps the query (and the using) and then does a yield return on each item the query returns. Use this as the datasource.

5 seems the best bet for most cases. It has the advantage of not changing the code much while not adding the (potentially large, depending on data) overhead of number 2. Note that just calling AsEnumerable (which has almost the same effect on execution order) would not have the same effect, as the closure would still be leaving the block unexecuted.

Edit: An enumerable that wraps the query would be like:

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

Then you would set change SessionSummary_Load to:

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

Hopefully this will solve the problem, because entities never leaves the scope of the using.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文