Как насчет этого?По сути, цикл сохранит предыдущий документ в памяти и обработает его на следующей итерации.Таким образом, как только он выходит из цикла, у него есть «последний документ», который можно передать, и он может пометить его как таковой для процессора.
public async Task GetDocs(string id, Func<Model, bool, Task> processor)
{
var filter = Builders<Model>.Filter;
var sort = Builders<Model>.Sort;
using (var cursor = await Coll.Find(filter.Eq(f => f.id, id)).ToCursorAsync())
{
Model previousDoc = null;
foreach (var doc in cursor.Current)
{
if (previousDoc != null)
{
await processor(previousDoc, false);
}
previousDoc = doc;
}
if (previousDoc != null)
{
await processor(previousDoc, true);
}
}
}
Вы также можете заключить его в метод многократного использования, который работаетс любым IEnumerable (здесь я использовал ValueTuples , но вы можете создать свой собственный тип, если не можете их использовать):
public static IEnumerable<(T Model, bool IsLast)> Map<T>(IEnumerable<T> items)
{
T prevModel = default(T);
bool isFirst = true;
foreach (var model in items)
{
if (!isFirst)
{
yield return (prevModel, false);
}
else
{
isFirst = false;
}
prevModel = model;
}
if (!isFirst)
{
yield return (prevModel, true);
}
}
public async Task GetDocs(string id, Func<Model, bool, Task> processor)
{
var filter = Builders<Model>.Filter;
var sort = Builders<Model>.Sort;
using (var cursor = await Coll.Find(filter.Eq(f => f.id, id)).ToCursorAsync())
{
foreach (var docWrapper in Map(cursor.Current))
{
await processor(docWrapper.Model, docWrapper.IsLast);
}
}
}