背景
大家好。我有一个抽象类叫做BaseRecordFetcher<TEntity>
,它有一个方法从子类中获取读取/排序/翻译/移动下一步方法,并将结果作为模型实体进行返回。
当我读取多个数据行时,它会正确地对每个实体执行 yield return
,然后在do...while
之后到达 Trace 消息而没有任何问题。
问题
然而,我注意到当我在集合上使用 IEnumerable.FirstOrDefault()
时,系统会假定不再需要更多的 yield returns
,并且它不会完成此方法的执行!
除了追踪输出返回的记录数量以外,我对这种行为并没有太大的问题,但是它让我想到:“如果我确实需要一些...让我们称之为 finally
代码怎么办?”
问题
是否有一种方法可以始终确保系统在 yield return
之后运行一些后处理代码?
代码
/// <summary>
/// Retrieve translated entities from the database. The methods used to do
/// this are specified from the child class as parameters (i.e. Action or
/// Func delegates).
/// </summary>
/// <param name="loadSubsetFunc">
/// Specify how to load a set of database records. Return boolean
/// confirmation that records were found.
/// </param>
/// <param name="preIterationAction">
/// Specify what should happen to sort the results.
/// </param>
/// <param name="translateRowFunc">
/// Specify how a database record should translate to a model entity.
/// Return the new entity.
/// </param>
/// <param name="moveNextFunc">
/// Specify how the database row pointer should move on. Return FALSE on a
/// call to the final row.
/// </param>
/// <returns>
/// A set of translated entities from the database.
/// </returns>
/// <example><code>
///
/// return base.FetchRecords(
/// _dOOdad.LoadFacilitySites,
/// () => _dOOdad.Sort = _dOOdad.GetAutoKeyColumn(),
/// () =>
/// {
/// var entity = new FacilitySite();
/// return entity.PopulateLookupEntity(
/// _dOOdad.CurrentRow.ItemArray);
/// },
/// _dOOdad.MoveNext);
///
/// </code></example>
protected virtual IEnumerable<TEntity> FetchRecords(
Func<bool> loadSubsetFunc, Action preIterationAction,
Func<TEntity> translateRowFunc, Func<bool> moveNextFunc)
{
// If records are found, sort them and return set of entities
if (loadSubsetFunc())
{
Trace.WriteLine(string.Format(
"# FOUND one or more records: Returning {0}(s) as a set.",
typeof(TEntity).Name));
int recordCount = 0;
preIterationAction();
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
// This code never gets reached if FirstOrDefault() is used on the set,
// because the system will assume no more enities need to be returned
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}
else
{
Trace.WriteLine(string.Format(
"# ZERO records found: Returning an empty set of {0}.",
typeof(TEntity).Name));
}
}
编辑(添加解决方案;感谢@Servy和@BenRobinson):
try
{
do
{
recordCount++;
var entity = translateRowFunc();
yield return entity;
}
while (moveNextFunc());
}
finally
{
// This code always executes, even when you use FirstOrDefault() on the set.
Trace.WriteLine(string.Format(
"# FINISHED returning records: {0} {1}(s) returned as a set.",
recordCount, typeof(TEntity).Name));
}