在LINQ-SQL中,将DataContext包装在using语句中 - 优缺点

23

请有人发表对于在LINQ-SQL中将DataContext包装在using语句中与不包装之间的优缺点的意见,包括性能、内存使用、编码容易度、正确性等方面的因素。

更新:在一个特定的应用程序中,我发现如果不将DataContext包装在using块中,由于实时对象没有被释放以进行垃圾回收,内存使用量会持续增加。例如,在下面的示例中,如果我保留q对象列表的引用并访问q的实体,则创建了一个不会被垃圾回收的对象图。

使用using的DataContext

    using (DBDataContext db = new DBDataContext())
    {
        var q = 
            from x in db.Tables
            where x.Id == someId
            select x;

        return q.toList();
    }

如何在不使用DataContext的情况下保持其活动状态?

  DBDataContext db = new DBDataContext()
  var q = 
        from x in db.Tables
        where x.Id == someId
        select x;

    return q.toList(); 

谢谢。


2
看起来是一个重复的问题:https://dev59.com/-HRA5IYBdhLWcg3wzhNY#821595 - devuxer
我想了解对内存的影响。 - hIpPy
请参考DanM的回答,类似于https://dev59.com/-HRA5IYBdhLWcg3wzhNY#821595。 - hIpPy
这可能看起来很傻,但是...我该如何获取DBDataContext?我在EF中创建了所有的基础结构,现在我正在尝试访问数据。我所拥有的只有这个DataContext context = new DataContext(ConfigurationManager.ConnectionStrings["ConnectionString"].ToString()); - Michel Ayres
5个回答

12

相对于其他事情,创建DataContext可能比较昂贵。然而,如果您使用结束并希望尽快关闭连接,则此操作将实现该目的,同时还释放上下文中缓存的任何结果。请记住,无论如何都会创建它,在这种情况下,您只是让垃圾收集器知道有更多的可用空间需要清除。

DataContext被设计成短期使用对象,使用它,完成工作,然后离开...这正是您在using语句中做的。

因此,优点如下:

  • 更快关闭连接
  • 释放dispose之后的内存(内容中的缓存对象)

缺点-代码更多?但这不应该是阻碍,您在此处正确地使用了using

在这里查看微软的答案:http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2625b105-2cff-45ad-ba29-abdd763f74fe

是否需要使用using/.Dispose()的简短版本:

简短的回答是,不需要,但您应该这样做...


2
显然,我们听到了不同的关于创建DataContext复杂性的说法。 - James Curran
@James - 虽然它不是很重,但相对于其他物品来说,它确实是比较重的,在我的当前应用程序中,它是最重要的项目,它很轻便,但仍然比普通对象更重。 - Nick Craver

5

嗯,它是一个IDisposable,所以我想这不是一个坏主意。MSFT的人们说他们尽可能地轻量化了DataContexts,以便您可以肆无忌惮地创建它们,所以您可能没有太多收益.....


这是我在其他地方问类似问题时得到的答案。基本上,它不会有什么坏处,可能会有帮助,但可能性不大。 - mark123
3
除了一个例外情况,你在单个方法中创建、使用和完成的所有IDisposable对象都应该在using块中使用。否则,作者不会选择实现IDisposable - John Saunders
我完全同意。始终努力做正确的事情。当我尝试使用using块并允许IoC容器(Castle Windsor)从存储库实例化项目时,我遇到了一些问题。在为此问题感到紧张时,有人告诉我IoC容器会处理Dispose()。我希望这是正确的,因为这确实很有道理。 - mark123

5
  1. 第一次 DataContext 会从数据库中获取对象。
  2. 下次您执行查询以获取相同的对象(使用相同的参数):您将在分析器中看到查询,但是您的 DataContext 中的对象不会被新的来自数据库的对象替换!

更不用说每个 DataContext 背后都有一个标识映射,其中包含您要从数据库中请求的所有对象(您不想保留这些对象)。

DataContext 的整个思想是“工作单元”和“乐观并发”。仅用于短事务(仅提交一次)并进行处理。

不忘记处理的最佳方法是使用 ()。


3

这取决于您的数据层的复杂性。如果每个调用都是简单的单个查询,那么每个调用都可以像您的问题中一样包含在Using中,那就没问题了。

另一方面,如果您的数据层可以从业务层期望多个连续调用,那么您将不断创建/处理每个更大序列的调用的DataContext,这并不理想。

我所做的是将我的数据层对象创建为IDisposible。当它被创建时,DataContext被创建(或者真正的说是当对方法的第一次调用发生时),当数据层对象被销毁时,它会关闭和处理DataContext。

看起来是这样的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;

namespace PersonnelDL
{
    public class PersonnelData : IDisposable
    {
        #region DataContext management
        /// <summary>
        /// Create common datacontext for all data routines to the DB
        /// </summary>
        private PersonnelDBDataContext _data = null;
        private PersonnelDBDataContext Data
        {
            get
            {
                if (_data == null)
                {
                    _data = new PersonnelDBDataContext(ConfigurationManager.ConnectionStrings["PersonnelDB"].ToString());
                    _data.DeferredLoadingEnabled = false; // no lazy loading
                    //var dlo = new DataLoadOptions(); // dataload options go here
                }
                return _data;
            }
        }

        /// <summary>
        /// close out data context
        /// </summary>
        public void Dispose()
        {
            if (_data != null)
                _data.Dispose();
        }
        #endregion

        #region DL methods
        public Person GetPersonByID(string userid)
        {
            return Data.Persons.FirstOrDefault(p => p.UserID.ToUpper().Equals(userid.ToUpper()));
        }

        public List<Person> GetPersonsByIDlist(List<string> useridlist)
        {
            var ulist = useridlist.Select(u => u.ToUpper().Trim()).ToList();
            return Data.Persons.Where(p => ulist.Contains(p.UserID.ToUpper())).ToList();
        }

        // more methods...
        #endregion
    }
}

1
在一个特定的应用程序中,我发现如果不将DataContext包装在using块中,则内存使用量会随着未释放的活动对象而持续增加。就像在下面的例子中一样,如果我保持对List<Table>对象的引用并访问q的实体,我就会创建一个不能被GC释放的对象图。
DBDataContext db = new DBDataContext()
var qs = 
    from x in db.Tables
    where x.Id == someId
    select x;

return qs.toList();

foreach(q in qs)
{
    process(q);
    // cannot dispose datacontext here as the 2nd iteration 
    // will throw datacontext already disposed exception 
    // while accessing the entity of q in process() function
    //db.Dispose();
}

process(Table q)
{
    // access entity of q which uses deferred execution
    // if datacontext is already disposed, then datacontext 
    // already disposed exception is thrown
}

鉴于这个例子,我不能处理数据上下文,因为列表变量qs中的所有Table实例**共享同一个数据上下文。在Dispose()之后,访问process(Table q)中的实体会抛出一个数据上下文已经被处理的异常。

对我来说,丑陋的解决方法是在foreach循环之后删除q对象的所有实体引用。更好的方法当然是使用using语句。

就我的经验而言,我建议使用using语句。


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