我有一个重写的SaveChangesAsync
EF方法如下:
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
也是我的类,有一些常见的属性,如CreatedOn
,每个实体都继承自这个类。这个覆盖的方法使用我自己的ForEach
扩展:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var item in enumerable)
{
action(item);
yield return item;
}
}
当存在yield语句时,foreach循环不会执行。如果我改成它:
public static IEnumerable<T> ForEach<T>(this IEnumerable<T> enumerable, Action<T> action)
{
foreach (var item in enumerable)
{
action(item);
}
return enumerable;
}
然后它完美地运作。为什么yield不会在迭代中执行它?
延期执行。代码在您使用产生的项目时执行,例如。通过循环结果或通过调用.ToList()
。在您的代码示例中,您似乎永远不会消耗迭代的结果,因此永远不会执行它。
因为迭代器需要迭代
打电话给ToList();
您使用yield return语句一次返回一个元素。
您可以使用foreach语句或LINQ查询来使用迭代器方法。 foreach循环的每次迭代都会调用迭代器方法。在迭代器方法中达到yield return语句时,将返回表达式,并保留代码中的当前位置。下次调用迭代器函数时,将从该位置重新开始执行。
简而言之
对ForEach
的调用不执行该方法的主体。相反,该调用返回一个IEnumerable,因为没有对它进行枚举,所以该方法甚至根本没有打到正文,因为它从未开始迭代。
如果你使用yield return
或yield break
,你的代码只会在有人迭代你从函数获得的IEnumerable<T>
时被执行。 yield
operator可以编写惰性代码。
将.ToList()
或.ToArray()
附加到语句中,但我建议使用.ToList()
,因为如果参数不是.ToArray()
类型,ICollection<T>
需要创建多个数组。 See