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

发布于 2024-09-02 01:48:40 字数 1666 浏览 3 评论 0原文

更新 - 答案显然是 DbLinq 没有正确实现 Dispose()。噢!


下面的内容都是误导性的 - 底线:DbLinq 还不等于 LinqToSql,正如我最初问这个问题时所假设的那样。请谨慎使用!

我正在将存储库模式与 DbLinq 结合使用。我的存储库对象实现了 IDisposable,而 Dispose() 方法只做一件事 - 在 DataContext 上调用 Dispose()代码>.每当我使用存储库时,我都会将其包装在 using 块中,如下所示:

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

此方法返回一个 IEnumerable,因此,如果我的理解正确,则不会查询数据库实际上会发生,直到遍历Enumerable(例如,通过将其转换为列表或数组或在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)后立即被调用,这是唯一一次它被调用。

那么,三个问题:

  1. 这是如何运作的?在 DataContext 被处置后,已处置的 DataContext 如何仍然查询数据库以获取结果?
  2. Dispose() 实际上做了什么?
  3. 我听说这是没有必要的(例如,请参阅 这个问题)来处理DataContext,但我的印象是这不是一个坏主意。有什么理由处置 DbLinq DataContext 吗?

Update - The answer is apparently that DbLinq doesn't implement Dispose() properly. D'oh!


The below is all sort of misleading - Bottom line: DbLinq is not (yet) equivalent to LinqToSql, as I assumed when I originally asked this question. Use it with caution!

I'm using the Repository Pattern with DbLinq. My repository objects implement IDisposable, and the Dispose() method does only thing--calls Dispose() on the DataContext. Whenever I use a repository, I wrap it in a using block, like this:

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

This method returns an IEnumerable<Person>, so if my understanding is correct, no querying of the database actually takes place until Enumerable<Person> is traversed (e.g., by converting it to a list or array or by using it in a foreach loop), as in this example:

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

In this example, Dispose() gets called immediately after setting persons, which is an IEnumerable<Person>, and that's the only time it gets called.

So, three questions:

  1. How does this work? How can a disposed DataContext still query the database for results after the DataContext has been disposed?
  2. What does Dispose() actually do?
  3. I've heard that it is not necessary (e.g., see this question) to dispose of a DataContext, but my impression was that it's not a bad idea. Is there any reason not to dispose of a DbLinq DataContext?

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

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

发布评论

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

评论(3

如若梦似彩虹 2024-09-09 01:48:40

1 这是如何工作的?已处置的 DataContext 如何在已处置后仍查询数据库以获取结果?

不起作用。有些东西你没有给我们看。我猜测您的存储库类没有在正确的时间正确/正确地处理 DataContext ,或者您在每个末尾都敷衍地编写了 ToList()查询,它完全否定了您通常获得的查询转换和延迟执行。

在测试应用程序中尝试以下代码,我保证你它将抛出一个ObjectDisposeException

// 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(),则查询结果已在 using内部枚举> 阻止,我敢打赌这就是你的情况所发生的情况。

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

除此之外,它还设置一个标志,指示 DataContext 已释放,在每个后续查询中都会检查该标志,并导致 DataContext 抛出 ObjectDisposeException > 带有消息对象名称:“Dispose 后访问了 DataContext。”。

如果 DataContext 打开连接并使其保持打开状态,它还会关闭连接。

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

需要Dispose DataContext,因为有必要Dispose所有其他IDisposable代码>.如果未能处置 DataContext,则可能会泄漏连接。如果从 DataContext 检索到的任何实体保持活动状态,您也可能会泄漏内存,因为上下文为其实现的工作单元模式维护内部身份缓存。但即使情况并非如此,您也不关心 Dispose 方法在内部执行的操作。假设它做了一些重要的事情。

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

如果您使用空的 Dispose 方法处置 IDisposable,可能发生的最糟糕的事情是浪费一些 CPU 周期。如果您未能使用非平凡的实现处置IDisposable,可能发生的最糟糕的事情就是泄漏资源。这里的选择是显而易见的;如果您看到 IDisposable,请不要忘记将其丢弃。

1 How does this work? How can a disposed DataContext still query the database for results after the DataContext has been disposed?

It doesn't work. There's something you're not showing us. I'm guessing that either your repository class doesn't dispose the DataContext properly/at the right time, or that you are perfunctorily writing ToList() at the end of every query, which completely negates the query transformation and deferred execution you normally get.

Try the following code in a test app, I guarantee you that it will throw an 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);
}

This is the simplest possible reproducible case, and it will always throw. On the other hand, if you write people = context.Person.ToList() instead, then the query results have already been enumerated inside the using block, which I'll bet is what's happening in your case.

2 What does Dispose() actually do?

Among other things, it sets a flag indicating that the DataContext is disposed, which is checked on every subsequent query and causes the DataContext to throw an ObjectDisposedException with the message Object name: 'DataContext accessed after Dispose.'.

It also closes the connection, if the DataContext opened it and left it open.

3 I've heard that it is not necessary (e.g., see this question) to dispose of a DataContext, but my impression was that it's not a bad idea. Is there any reason not to dispose of a LinqToSql DataContext?

It is necessary to Dispose the DataContext, as it is necessary to Dispose every other IDisposable. You could potentially leak connections if you fail to dispose the DataContext. You could also leak memory if any of the entities retrieved from the DataContext are kept alive, since the context maintains an internal identity cache for the unit-of-work pattern it implements. But even if none of this were the case, it is not your concern what the Dispose method does internally. Assume that it does something important.

IDisposable is a contract that says, "cleanup may not be automatic; you need to dispose me when you're finished." You have no guarantees of whether or not the object has its own finalizer that cleans up after you if you forget to Dispose. Implementations are subject to change, which is why it's not a good idea to rely on observed behaviour as opposed to explicit specifications.

The worst thing that can happen if you dispose an IDisposable with an empty Dispose method is that you waste a few CPU cycles. The worst thing that can happen if you fail to dispose an IDisposable with a non-trivial implementation it is that you leak resources. The choice here is obvious; if you see an IDisposable, don't forget to dispose it.

苏大泽ㄣ 2024-09-09 01:48:40

“persons”是一个 IEnumerable 集合,DataContext(存储库)只需要进行 .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/库/system.linq.enumerable_members.aspx

"persons" is an IEnumerable collection, the DataContext (repository) is only required to make the .GetNew call.

the from/select/etc keywords are syntactic sugar for extension methods added in the System.Linq namespace. These extension methods add the IEnumerable functionality you are using in your query, not the DataContext. In fact, you can do all of this without using LINQ2SQL at all, by programatically creating an IEnumerable to demonstrate.

If you attempt to make any further repository (DataContext) calls using these objects, that's when you'll receive an error.

The IEnumerable collection will contain ALL records from your repository, this is why you don't require the DataContext to make the query.

Extension methods: http://msdn.microsoft.com/en-us/library/bb383977.aspx

LINQ extension methods:
http://msdn.microsoft.com/en-us/library/system.linq.enumerable_members.aspx

来世叙缘 2024-09-09 01:48:40

在 API 深处,您可能会看到一种使用如下 api 的方法:

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

该命令执行时,关联的 Connection 对象在以下情况下关闭关联的 DataReader 对象已关闭。

Deep inside the API, you will probably see a method using an api like this one:

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

When the command is executed, the associated Connection object is closed when the associated DataReader object is closed.

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