XardasLord
XardasLord

Reputation: 1937

yield return in custom foreach not working as expected

I have an overrided SaveChangesAsync EF method as following:

    public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))
    {
        ChangeTracker
            .Entries()
            .Where(e => e.State == EntityState.Added)
            .Select(e => e.Entity as BaseEntity)
            .ForEach(e => e.ModifiedOn = e.CreatedOn = DateTimeOffset.Now);

        return base.SaveChangesAsync(cancellationToken);
    }

BaseEntity is also my class with some common properties like CreatedOn and every entity inherits from this class. This overrided method uses my own ForEach extension:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (var item in enumerable)
        {
            action(item);
            yield return item;
        }
    }

And the foreach loop is not executing when there is a yield statement. If I change it to this:

    public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
    {
        foreach (var item in enumerable)
        {
            action(item);
        }

        return enumerable;
    }

Then it works perfectly. Why does yield is not executing it this iteration?

Upvotes: 0

Views: 1410

Answers (3)

Ackdari
Ackdari

Reputation: 3498

If you use yield return or yield break your code will only be executed if someone iterates over the IEnumerable<T> you get from the function. The yieldoperator makes it possible to write lazy code.

Append .ToList() or .ToArray() to the statement, but I recommend .ToList(), because .ToArray() needs to create multiple arrays if the argument is not of the type ICollection<T>. See

Upvotes: 1

JacquesB
JacquesB

Reputation: 42639

Deferred execution. The code is executed at the time when you consume the yielded items, eg. by looping over the result or by calling .ToList() on it. In your code example it seems like you never consume the result of the iteration, so it is never executed.

Upvotes: 5

TheGeneral
TheGeneral

Reputation: 81493

Because an iterator needs to iterate

Call ToList();

yield (C# Reference)

You use a yield return statement to return each element one at a time.

You consume an iterator method by using a foreach statement or LINQ query. Each iteration of the foreach loop calls the iterator method. When a yield return statement is reached in the iterator method, expression is returned, and the current location in code is retained. Execution is restarted from that location the next time that the iterator function is called.

In short

The call to ForEach doesn't execute the body of the method. Instead the call returns an IEnumerable, Since nothing enumerates over it, the method doesn't even hit the body at all as it has never started iterating.

Upvotes: 2

Related Questions