如何确保在延迟执行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是基于延迟加载的。当您调用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));
}
}