以下代码中为什么要使用“using语句”?

3

我不完全理解为什么我们要在这里使用“using”块。

以下代码是使用EF从数据库中显示两个表名为“genre”和“review”的代码:

代码如下:

C#

protected void Page_Load(object sender, EventArgs e)    
{    
    using (PlanetWroxEntities myEntities = new PlanetWroxEntities())    
    {
        var authorizedReviews = from review in myEntities.Reviews    
                                where review.Authorized == true    
                                orderby review.CreateDateTime descending    
                                select review;

        GridView1.DataSource = authorizedReviews.ToList();

        GridView1.DataBind();    
    }    
}

以下是作者的一些解释:

生成模型后,您可以对其执行LINQ查询以从底层数据库中获取数据。要访问数据,需要一个 DbContext 类型的实例,该类是 PlanetWroxEntities 类的基类。在代码中,这个实例是在 Using 块内创建的。Using 块(C# 中的 using)用于包含必须放入内存并且使用完毕后必须清除(从内存中清除)的变量。因为 myEntities 变量保存了对 SQL Server 数据库的稀缺连接,所以最好将使用它的代码包装在 Using 块中,以便对象在块结束时被销毁而连接被释放。此 myEntities 对象暴露数据(如评论和类型),您可以使用这些数据来进行查询:

我有两个问题:

1- 为什么我们应该摆脱这个变量?是因为它占用了内存空间吗?还是因为它保存了与 SQL Server 数据库的连接,所以我们不需要对象而只需要连接?

2- "连接被释放" 是什么意思?如果连接是对象的一部分,为什么它没有被销毁?


myEntities 只能在 using 块内使用,并且在块结束后会被清除。不需要关闭或断开连接。+ - Arun Prasad E S
为什么对象被销毁后连接还能保持? - mohsen doraghi
那是我的理论知识,好问题。 - Arun Prasad E S
发布测试代码。 - Arun Prasad E S
一个好的经验法则是:如果某个东西实现了 IDisposable 接口,即使你不完全理解该对象持有何种非托管资源,你也应该使用 using 语句来清理它。 - Falanwe
显示剩余4条评论
3个回答

3
使用using语句的原因是确保myEntities.Dispose()方法在using语句内的代码完成后立即调用。这是必要的,因为PlanetWroxEntities类的基类是一个DbContext,它持有与数据库的连接。
数据库连接是一种珍贵的资源,它们是从数据库连接池中获取的,并且只有有限数量的连接可供整个应用程序共享。如果您不通过使用using语句(或其他方式)在使用完myEntities对象后立即调用Dispose,则myEntities对象将继续占用该数据库连接,直到.NET内存管理器的垃圾回收代码开始回收不再使用的myEntities对象所持有的内存,这需要一段不确定的时间。
内存管理器将在那时调用Dispose方法。一旦调用Dispose方法,数据库连接就从对象中释放,并返回到数据库连接池中,当您调用获取数据库连接的方法(例如创建PlanetWroxEntities类的其他实例时)时,它可以被应用程序的其他部分获取。
使用using语句的目的是在您完成对象后立即调用Dispose方法,以便在这种情况下,数据库连接会立即返回到池中。
2)它意味着数据库连接已被您的应用程序释放,并返回到数据库连接池中,现在其他部分的应用程序可以获得它。这里有一个资源,讲解更多关于连接池的内容,它将为您提供更多底层的信息:https://msdn.microsoft.com/en-us/library/8xx3tyca.aspx 在幕后,使用语句被实现为finally语句。因此,这段代码:
using (PlanetWroxEntities myEntities = new PlanetWroxEntities())    
{
    var authorizedReviews = from review in myEntities.Reviews    
                            where review.Authorized == true    
                            orderby review.CreateDateTime descending    
                            select review;

    GridView1.DataSource = authorizedReviews.ToList();

    GridView1.DataBind();    
} 

变成

PlanetWroxEntities myEntities; 
try{
    myEntities = new PlanetWroxEntities();
    var authorizedReviews = from review in myEntities.Reviews    
                            where review.Authorized == true    
                            orderby review.CreateDateTime descending    
                            select review;

    GridView1.DataSource = authorizedReviews.ToList();

    GridView1.DataBind();   

} finally{
    myEntities.Dispose();
}

在早期的C#中,我们总是需要自己编写finally语句来释放可处理对象的非托管资源。但后来,using语句被引入到C#中,以使这一过程更加简便。即使今天,如果您愿意,仍然可以自己编写finally语句,但大多数人喜欢使用using语句。
了解更多信息,请参见https://msdn.microsoft.com/en-us/library/yh598w02.aspx

谢谢。这是最好的且全面的解释。换句话说,“using”作为一个计时器,可以在对象完成后释放连接。将“using”实现在其他“using”所在的命名空间中是否会产生与您描述的相同结果?或者它应该像上面的例子一样与实例类一起使用?在我正在阅读的书中,这方面的解释不是很多,虽然它与这个主题无关,但您能否向我介绍一些有关ASP和.NET的书籍和资源呢? - mohsen doraghi
谢谢。Using语句并不像计时器,它是另一种编写finally语句的方式,有点类似。我会更新我的答案来展示这一点。Using仅在实例化一个类时使用,特别是实现Dispose方法的类。SO是学习的好资源,Google是你最好的朋友。话虽如此,我喜欢apress、微软出版社和SAMS的编程书籍。在Twitter上@dotnetcore给我发推文,我可以给更多的建议。 - RonC

2

PlanetWroxEntities 实现了 IDisposable 接口。

IDisposable 被实现于持有需要在对象使用完毕后释放的本地资源的类中。

因此,回答问题1:你应该尽快“摆脱”变量,这样你就不会持有可以释放回系统的本地资源。

using 语句确保在使用实体完成后正确调用 Dispose() 方法。你不仅释放连接,还释放其他任何本地资源。


关于前面的例子,本地资源是什么? - mohsen doraghi
通过“本地资源”@justin的意思是“非托管资源”,在这种情况下,它指的是数据库连接。 - RonC

1

为什么我们应该摆脱变量?

是因为它占用内存空间吗?

是的。

还是因为它保持与SQL Server数据库的连接,所以我们不需要对象而只需要连接?

作业(填充网格)在GridView1.DataBind(); 中完成。

在此之后,您不需要对象或连接。

"连接已释放"是什么意思?如果连接是对象的一部分,为什么它没有被销毁?

对象myEntities被销毁,同时它与数据库的连接也被销毁。

通常,EF查询返回的实体可以生成进一步的数据库查询。这称为延迟加载

将EF查询包装在using语句中可防止发生延迟加载-让您确切地知道数据库查询仅在存储库内发生。


谢谢,我明白了。据我所知,惰性加载意味着子对象在你明确告诉它们之前不会被加载,并且你查询的子对象的属性为null。你不觉得你的定义是错误的,或者至少你应该说:“通常情况下,由EF查询返回的实体如果我们明确希望它们可以生成进一步的数据库查询?所以通常它们不会生成进一步的数据库查询。或者我错了吗? - mohsen doraghi
延迟加载意味着在第一次使用时加载(例如,当您测试它们为 null 时)。因此,如果数据库连接仍然存在,if (x.SubObject == null) 将在那个时候访问数据库。 - user1023602

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接