【问题标题】:Returning IQueryable from within a using block. Need better design从 using 块中返回 IQueryable。需要更好的设计
【发布时间】:2013-06-10 12:54:20
【问题描述】:

我创建了一个电话簿风格的应用程序;在我的电话簿对象上,我有一个本地成员 _site 用作过滤器,因为大约有 1000 个电话号码,分布在我组织内的 12 个站点中。使用此方法一次只能检索一个站点。

这是我最初的方法。 GUI 有多种重新排序数据的方法,因此我将其保留为 IQueryable,因为我想推迟 SQL 以允许在 SQL 服务器而不是客户端 PC 上完成过滤。

作品

public IQueryable<PhoneNumber> GetPhoneDirectory()
{
    PhoneBookDataContext db = new PhoneBookDataContext())
    return db.PhoneNumbers.Where(d => d.Site == _site);
}

但是,我也试图在 using 声明方面保持“最佳实践”。

不起作用

public IQueryable<PhoneNumber> GetPhoneDirectory()
{
    using (PhoneBookDataContext db = new PhoneBookDataContext())
    {
        return db.PhoneNumbers.Where(d => d.Site == _site);
    }
}

现在正如@justanotheruseryoumay 所指出的,这将导致异常,因为数据上下文在访问对象时已被释放。

我想我要问的是,当我不能使用“使用”语句并且不严格知道上下文何时完成时,如何确保我的数据上下文得到很好的处理。

【问题讨论】:

  • 虽然我不能确定,但​​我认为问题在于 using 语句将无法 Dispose 对象,直到它超出范围 - 这不会发生直到查询被执行。
  • 如果我在这里错了,有人纠正我,但是在方法返回的第二个地方,是否会标记任何局部变量以进行处置?我假设上面的两个 sn-ps 基本上是等价的。
  • 嗨,我已经编辑了我的问题,因为我没有明确提到第二个会引发异常。
  • 你应该让@justanotheruseryoumay知道他们的答案似乎是正确的。
  • 他们删除了,不知道怎么联系他们。

标签: c# linq linq-to-sql using-statement


【解决方案1】:

如果您想返回IQueryable,您可以将包含GetPhoneDirectory 的类设为一次性,将PhoneBookDataContext 设为一个字段,然后在您的 dispose 方法中将其处置。

然后,您将让调用者有责任处置他的类实例。

例如

public class MyClass : IDisposable
{
    PhoneBookDataContext db;

    public MyClass()
    {
        db = new PhoneBookDataContext();
    }

    public IQueryable<PhoneNumber> GetPhoneDirectory()
    {
        return db.PhoneNumbers.Where(d => d.Site == _site);
    }

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

// Caller
using(var myClass = new MyClass())
{
    var queryable = myClass.GetPhoneDirectory();
    ...
}

【讨论】:

  • 啊我喜欢这个样子!我唯一的问题是,如果其他人在某人创建“MyClass”然后调用该方法之间将一条记录插入到 PhoneNumbers 中会发生什么?大概我总是会收到新的 SQL 调用?
【解决方案2】:

查询的执行仍将被推迟,PhoneBookDataContext 仍将被正确处理,因为编译器将 using 解释为 try/finally。当您实际执行查询时,它将导致运行时错误,因为 PhoneBookDataContext 不再存在。我建议对您的查询执行 .ToList() 并以这种方式返回。如果您想在退货后更改订单,那么您仍然可以随意对其进行 LINQ。

编辑: 您可以做的另一件事是在调用方法中使用 PhoneBookDataContext 创建 using 并传入上下文。无论如何,上下文确实会在该方法中使用,只要您需要它就可以保留它,并坚持使用良好的使用格式。

【讨论】:

    【解决方案3】:

    是的;这是一个糟糕的设计,因为您的 IQueryable&lt;PhoneNumber&gt; 只有在您调用导致它被评估的方法时才会被评估,例如 ToList() 或当您使用 foreach 迭代它时。

    在您的代码中,您将返回一个尚未评估的IQueryable&lt;PhoneNumber&gt;,并且在调用者有机会执行它之前,它的内部负责向您提供记录(db);已经处置了。

    作为一个建议:

    public IEnumerable<PhoneNumber> GetPhoneDirectory()
    {
        using (PhoneBookDataContext db = new PhoneBookDataContext())
        {
            return db.PhoneNumbers.Where(d => d.Site == _site).ToList();
        }
    }
    

    或者将 db 对象重新定位到设计中的其他位置(工作单元和存储库是很好的模式,可以查看恕我直言)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-10-14
      • 2012-05-19
      • 1970-01-01
      • 2020-01-30
      • 1970-01-01
      • 2020-05-19
      • 2021-04-30
      • 1970-01-01
      相关资源
      最近更新 更多