DataContext的“Using”语句的正确使用方法

8

我正在使用Linq to Entities,最近发现很多人推荐像这样在using语句中包装datacontext:

Using(DataContext db = new DataContext) {
    var xx = db.customers;
}

这很有道理。然而,我不确定如何将这个实践应用到我的模型中。 例如:我有一个名为“customer”的接口,它是由一个存储库实现的,像这样:
namespace Models
{
    public class rCustomer : iCustomer
    {

        readonly DataContext db = new DataContext();

        public customer getCustomer(Guid id)
        {
            return db.customers.SingleOrDefault(por => por.id == id);
        }

        public iQueryable<customer> getTopCustomers()
        {
            return db.customers.Take(10);
        }

        //*******************************************
        //more methods using db, including add, update, delete, etc.
        //*******************************************

    }
}

然后,为了充分利用使用,我需要将方法更改为以下形式:
namespace Models
{
    public class rCustomer : iCustomer
    {
        public customer getCustomer(Guid id)
        {
            using(DataContext db = new DataContext()) {       
                return db.customers.SingleOrDefault(por => por.id == id);
            } 
        }

        public iQueryable<customer> getTopCustomers()
        {
            using(DataContext db = new DataContext()) {       
                 return db.customers.Take(10);
            } 
        }

        //*******************************************
        //more methods using db
        //*******************************************

    }
}

我的问题是:“使用”这种方法的推荐真的那么好吗?请考虑到这一变化将是一个重大的改变,我有大约25个接口/库组合,每个都有大约20-25个方法,更不用说完成后需要重新测试所有内容了。还有其他方法吗?
谢谢!
Edgar.

这取决于你如何使用那个类。 - SLaks
如果你知道 using 的作用,那么决定是否值得使用就取决于你了。 - gdoron
5个回答

5
你可以实现一个数据库工厂,这将导致你的DbContext被重用
你可以按照以下方式实现:
DatabaseFactory类:
public class DatabaseFactory : Disposable, IDatabaseFactory
{
    private YourEntities _dataContext;
    public YourEntities Get()
    {
        return _dataContext ?? (_dataContext = new YourEntities());
    }
    protected override void DisposeCore()
    {
        if (_dataContext != null)
            _dataContext.Dispose();
    }
}

代码库基类摘录:

 public abstract class Repository<T> : IRepository<T> where T : class
{
    private YourEntities _dataContext;
    private readonly IDbSet<T> _dbset;
    protected Repository(IDatabaseFactory databaseFactory)
    {
        DatabaseFactory = databaseFactory;
        _dbset = DataContext.Set<T>();
    }

    protected IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    protected YourEntities DataContext
    {
        get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
    }

您的表格存储库类:
public class ApplicationRepository : Repository<YourTable>, IYourTableRepository
{
    private YourEntities _dataContext;

    protected new IDatabaseFactory DatabaseFactory
    {
        get;
        private set;
    }

    public YourTableRepository(IDatabaseFactory databaseFactory)
        : base(databaseFactory)
    {
        DatabaseFactory = databaseFactory;
    }

    protected new YourEntities DataContext
    {
        get { return _dataContext ?? (_dataContext = DatabaseFactory.Get()); }
    }

   }
    public interface IYourTableRepository : IRepository<YourTable>
   {
   }
}

这个与AutoFac构造函数注入完美配合使用。

3
正如其他人所提到的,数据上下文被释放很重要。我不会再进一步解释这个问题。
我看到有三种可能的设计来确保上下文被释放:
  1. 您在rCustomer方法的每个作用域内创建一个数据上下文,以便每个数据上下文都在using块中。
  2. 将数据上下文保留为实例变量,并使rCustomer实现IDisposable接口,以便当rCustomer被释放时,可以同时释放其数据上下文。这意味着所有rCustomer实例都需要包装在using块中。
  3. 通过构造函数将现有数据上下文的实例传递给rCustomer。如果这样做,那么rCustomer就不需要负责释放它,类的使用者将会处理它。这将允许您在几个rCustomer实例之间或多个需要访问数据上下文的类之间使用单个数据上下文。这具有优点(创建新数据上下文涉及的开销较小)和缺点(数据上下文倾向于通过缓存等方式占用大量内存,因此内存占用更大)。
我真诚地认为选项1是一个相当不错的选择,只要您没有发现它运行太慢(如果您认为它会引起问题,请进行测时/性能分析)。由于连接池技术,这应该不会有太大问题。如果出现问题,我会选择选项3作为下一个选择。选项2不算太差,但对于团队中的其他成员来说可能会有点尴尬和意外。

3

从您提供的代码中,我发现您明确地使用了readonly DataContext db = new DataContext(); 作为全局变量,因此您考虑在rCustomer类实例生命周期内拥有该对象的生命周期。

如果这是真的,您可以实现IDisposable,并在Dispose()内部编写如下代码,而无需重写所有内容:

private void Dispose()
{
    if(db  != null) 
        db.Dispose();
}

希望这能帮到你。

2

DataContext类被包装在Using语句中,因为它实现了IDisposable接口。

在DataContext内部,它使用SqlConnection对象和SqlCommand对象。为了正确释放这些连接回Sql Connection池,它们需要被处理掉。

垃圾收集器最终会完成这个任务,但由于IDisposable对象的管理方式,需要进行两次传递。

强烈建议调用Dispose,并且Using语句是一个不错的选择。

阅读以下链接以获取更深入的解释:

http://social.msdn.microsoft.com/Forums/en-US/adodotnetentityframework/thread/2625b105-2cff-45ad-ba29-abdd763f74fe/

http://www.c-sharpcorner.com/UploadFile/DipalChoksi/UnderstandingGarbageCollectioninNETFramework11292005051110AM/UnderstandingGarbageCollectioninNETFramework.aspx


2

另一个选择是让您的rCustomer类实现IDisposable,然后在您的Dispose方法中,如果DataContext不为null,就调用它的Dispose。然而,这只是将可释放模式从您的rCustomer类推到使用rCustomer的任何类型中。


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