如何确保在延迟执行linq查询时关闭数据库连接

本文关键字:查询 linq 数据库连接 执行 延迟 何确保 确保 | 更新日期: 2023-09-27 17:57:56

我有以下方法:

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>的类。因此,r就是DbDataReader。我希望它执行声明了查询语法的查询,并返回一个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测试中,应用程序将运行得很好。我只是想防止数据库连接处于打开状态。

如何确保在延迟执行linq查询时关闭数据库连接

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

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

返回List<Foo>

显然,这不是你要做的。您正在返回IEnumerable<Foo>。如果您返回List<Foo>,那么您的开发人员除了调用ToList之外别无选择。

如果你真的不想这样做,我建议创建某种QueryObject,它将包含你在方法中的逻辑。然后通过某种helper类调用它,它会在内部调用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));
    }
}