为什么在DataContext上调用Dispose()后可以枚举DbLinq查询?

时间:2010-05-17 18:38:28

标签: c# dblinq

更新 - 答案显然是DbLinq没有正确实施Dispose()。 D'哦!


以下是所有类型的误导性 - 底线:DbLinq(尚未)等同于LinqToSql,正如我最初提出这个问题时所假设的那样。请谨慎使用!

我正在使用DbLinq的Repository Pattern。我的存储库对象实现了IDisposable,而Dispose()方法只做了事情 - 在Dispose()上调用DataContext。每当我使用存储库时,我将其包装在using块中,如下所示:

public IEnumerable<Person> SelectPersons()
{
    using (var repository = _repositorySource.GetPersonRepository())
    {
        return repository.GetAll(); // returns DataContext.Person as an IQueryable<Person>
    }
}

此方法返回IEnumerable<Person>,因此如果我的理解正确,则在遍历Enumerable<Person>之前不会实际查询数据库(例如,通过将其转换为列表或数组或使用它在foreach循环中),如下例所示:

var persons = gateway.SelectPersons();
// Dispose() is fired here
var personViewModels = (
    from b in persons
    select new PersonViewModel
    {
        Id = b.Id,
        Name = b.Name,
        Age = b.Age,
        OrdersCount = b.Order.Count()
    }).ToList(); // executes queries

在此示例中,Dispose()设置persons后会立即调用IEnumerable<Person>,这是唯一被调用的时间。

所以,有三个问题:

  1. 这是如何工作的?处置DataContext之后处置的DataContext如何仍然在数据库中查询结果?
  2. Dispose()实际上做了什么?
  3. 我听说没有必要(例如,见this question)处置DataContext,但我的印象是这不是一个坏主意。有没有理由处理DbLinq DataContext

3 个答案:

答案 0 :(得分:3)

  

1这是如何工作的?在处理DataContext之后,处理后的DataContext如何仍然在数据库中查询结果?

有效。有些东西你没有向我们展示。我猜你的存储库类没有在正确的时间正确/处理DataContext,或者你在每个查询结束时都在执行ToList(),这完全否定了查询转换通常得到的延期执行。

在测试应用中尝试以下代码,我保证您将抛出ObjectDisposedException

// Bad code; do not use, will throw exception.
IEnumerable<Person> people;
using (var context = new TestDataContext())
{
    people = context.Person;
}
foreach (Person p in people)
{
    Console.WriteLine(p.ID);
}

这是最简单的可重现的情况,它总是会抛出。另一方面,如果您改为编写people = context.Person.ToList(),那么查询结果已经在<{em> using区块中进行了枚举,我敢打赌,在您的情况下会发生什么

  

2 Dispose()实际上做了什么?

除此之外,它还设置了一个标志,表示DataContext已被处理,在每个后续查询中都会检查该标志,并导致DataContextObjectDisposedException投掷Object name: 'DataContext accessed after Dispose.'.消息{{ 1}}

如果DataContext打开它并将其打开,它也会关闭连接。

  

3我听说处理DataContext没有必要(例如,看到这个问题),但我的印象是这不是一个坏主意。有没有理由不处理LinqToSql DataContext?

<{1}} Dispose需要 ,因为DataContext每隔一个Dispose都需要{strong} 。如果您未能处置IDisposable,则可能会泄漏连接。如果从DataContext检索的任何实体保持活动状态,您也可能泄漏内存,因为上下文为其实现的工作单元模式维护内部标识缓存。但即使不是这种情况,也不是你关心的 DataContext方法在内部做什么。假设它做了一些重要的事情。

Dispose是一个合同,上面写着“清理可能不是自动的;你需要在完成后处理我。”如果忘记IDisposable,您无法保证对象是否有自己的终结器可以在您之后清理。实现可能会发生变化,这就是为什么依赖观察到的行为而不是明确的规范并不是一个好主意。

如果使用空Dispose方法处置IDisposable,最糟糕的事情就是浪费了几个CPU周期。如果您失败使用非平凡的实现来处置Dispose,那么最糟糕的事情就是泄漏资源。这里的选择很明显;如果您看到IDisposable,请不要忘记将其丢弃。

答案 1 :(得分:0)

“persons”是一个IEnumerable集合,DataContext(repository)只需要进行.GetNew调用。

from / select / etc关键字是System.Linq命名空间中添加的扩展方法的语法糖。这些扩展方法添加您在查询中使用的IEnumerable功能,而不是DataContext。事实上,你可以在不使用LINQ2SQL的情况下完成所有这些工作,通过编程创建一个IEnumerable来演示。

如果您尝试使用这些对象进行任何进一步的存储库(DataContext)调用,那么您将收到错误。

IEnumerable集合将包含存储库中的所有记录,这就是您不需要DataContext进行查询的原因。

扩展方法:http://msdn.microsoft.com/en-us/library/bb383977.aspx

LINQ扩展方法: http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx

答案 2 :(得分:0)

在API内部,您可能会看到一个使用类似api的方法:

http://msdn.microsoft.com/en-us/library/y6wy5a0f(v=VS.100).aspx

执行命令时,关闭关联的DataReader对象时关闭关联的Connection对象。