如何通过延迟执行linq查询来确保关闭我的数据库连接?

时间:2011-05-31 05:11:37

标签: c# linq linq-to-objects

我有以下方法:

public IEnumerable<Foo> GetFoo(int x, string y) 
{
    return from r in new GetFoo(x, y)
           select new Foo 
           {
               x = r.Get<int>("x"),
               y = r.Get<string>("y"),
               z = r.Get<DateTime?>("z"),
           };
 }

GetFoo是一个包含存储过程并实现IEnumerable<DbDataReader>的类。因此,rDbDataReader。 我希望它执行查询语法的查询并返回List<Foo> 如果没有这样做,我的理解是调用者可能根本不迭代整个列表,然后数据库连接将不会被关闭。我知道我可以把它放在括号中并调用ToList()但我想尽可能避免这种情况。我们有200多个存储过程,开发人员很容易错过添加ToList()。

我能做些什么来实现我想要的东西吗?

更新

这不是linq to sql,我创建了一个实现GetFoo的自定义类IEnumerable<DbDataReader>。返回的IEnumerator<DbDataReader>如下所示:

private class Enumerator : IEnumerator<DbDataReader> {
    DbDataReader _;
    public Enumerator(DbDataReader r) { _ = r; }
    public DbDataReader Current { get { return _; } }

    public void Dispose() { _.Dispose(); }

    object System.Collections.IEnumerator.Current { get { return _; } }
    public bool MoveNext() { return _.Read(); }
    public void Reset() { throw new NotImplementedException(); }
}

更新2

我无法强制执行调用ToList()和/或要求List<Foo>成为此方法的返回类型,以及其他类似的方法。开发人员审核应该抓住不将其转换为列表。不幸的是,如果错过那里并在QA测试中,应用程序将运行良好。我只是想阻止数据库连接可以保持打开状态。

3 个答案:

答案 0 :(得分:1)

Linq基于延迟加载。当您调用ToList时,它会执行命令。否则它不会执行它。所以你的问题的答案是否定的。但是,我不确定,也许代码合同可以帮助您标记方法,或者在返回List的函数中调用上面的代码。在这种情况下,开发人员将被绑定返回List。否则代码将无法编译。

答案 1 :(得分:0)

您需要更改返回类型以实现此目的。您可以将所有此类方法的返回类型更改为List<T>,以便开发人员必须以其他方式调用ToList,编译器将抛出错误或返回IEnumerable<DbDataReader>的方法应返回List<DbDataReader>其中DbDataReader是在内存数据读取器中实现的自定义,它是通过读取实际读者的数据生成的,这样您就不必担心读者会被打开。

答案 2 :(得分:0)

  

返回List<Foo>

显然,这不是你在做什么。您正在返回IEnumerable<Foo>。如果您返回List<Foo>,则开发人员无法致电ToList

如果你真的不想这样做,我会建议创建某种QueryObject,它将包含你方法中的逻辑。然后通过某种辅助类调用它,它会在内部调用ToList。这也符合DRY原则。

此外,您似乎只实现了对返回数据集的某种解析。所以你只需要实现这个解析:

public abstract class BaseStoredProcedureQuery<TReturn>
{
    protected abstract TReturn ParseRecord(DbDataReader r);

    protected IEnumerable<TReturn> ParseQuery(IEnumerable<DbDataReader> readers)
    {
        return readers.Select<DbDataReader,TReturn>(ParseRecord).ToList();
    }
}

public class Query1 : BaseStoredProcedureQuery<Foo>
{
    protected override Foo ParseRecord(DbDataReader r)
    {
        return new Foo
                   {
                       x = r.Get<int>("x"),
                       y = r.Get<string>("y"),
                       z = r.Get<DateTime?>("z"),
                   };
    }

    public IEnumerable<Foo> GetFoo(int x, string y)
    {
        return ParseQuery(GetFoo(x, y));
    }
}