从using块中返回IQueryable。需要更好的设计

时间:2013-06-10 12:54:20

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

我创建了一个PhoneBook风格的应用程序;在我的电话簿对象上,我有一个本地成员_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所指出的那样,这将导致异常,因为datacontext是在访问对象时被处置的。

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

3 个答案:

答案 0 :(得分:5)

如果你想返回IQueryable,你可以让你的班级包含GetPhoneDirectory一次性,将PhoneBookDataContext作为一个字段,并将其处理在你的处置方法中。

然后,您将把责任放在调用者身上以处理他的班级实例。

E.g。

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();
    ...
}

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

是;这是糟糕的设计,因为只有当您调用导致其被评估的方法(例如IQueryable<PhoneNumber>或使用ToList()进行迭代时)才会评估foreach

在您的代码中,您将返回尚未评估的IQueryable<PhoneNumber>,并且在调用者有机会执行它之前,它的内部负责向您发出记录(db);已被处置。

正如建议一样:

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

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