1

I am saving a bunch of items to my database using async saves

var tasks = items.Select(item =>
                         {
                             var clone = item.MakeCopy();
                             clone.Id = Guid.NewGuid();

                             return dbAccess.SaveAsync(clone);
                         });

                        await Task.WhenAll(tasks);

I need to verify how many times SaveAsync was successful (It throws and exception if something goes wrong). I am using IsFaulted flag to examine the tasks:

                        var successCount = tasks.Count(t => !t.IsFaulted);

Collection items consists of 3 elements so SaveAsync should have been called three times but it is executed 6 times. Upon closer examination I noticed that counting non-faulted tasks with c.Count(...) causes each of the task to re-run.

I suspect it has something to do with deferred LINQ execution but I am not sure why exactly and how to fix this.

Any suggestion why I observe this behavior and what would be the optimal pattern to avoid this artifact?

AstroSharp
  • 1,872
  • 2
  • 22
  • 31

1 Answers1

3

It happens because of multiple enumeration of your Select query.

In order to fix it, force enumeration by calling ToList() method. Then it will work correctly.

var tasks = items.Select(item =>
                     {
                         var clone = item.MakeCopy();
                         clone.Id = Guid.NewGuid();
                         return dbAccess.SaveAsync(clone);
                     })
                 .ToList();

Also you may take a look at these more detailed answers:

Community
  • 1
  • 1
Kote
  • 2,116
  • 1
  • 19
  • 20
  • 4
    Linked answers currently lacks the mention of the actual feature causing this behavior, which is [deferred execution](http://blogs.msdn.com/b/charlie/archive/2007/12/09/deferred-execution.aspx). Linq `IEnumerable`/`IQueryable` extensions are by default 'deferred executed', which means their actual execution does not occurs when those extensions are called, but when iterating other their resulting `IEnumerable`/`IQueryable`. And their execution re-occurs also again each time their resulting `IEnumerable`/`IQueryable` are re-enumerated. – Frédéric Feb 25 '16 at 19:45