[英]EF core using Async Await pattern throws already an open DataReader error
因此,错误基本上是实体框架中提到的,已经有一个与此连接关联的打开的 DataReader 必须先关闭 - VoidCC
在这种情况下,我根据解释了解发生了什么以及错误的原因。
然后我最近在应用http://www.asyncfixer.com/建议的最佳实践后遇到了同样的错误
该代码是一个自定义本地化程序,它使用 SQL 数据库作为它的存储而不是资源文件。 简而言之,如果它尚未缓存在 memory 中,它会提供来自 db 的字符串。 此外,如果字符串不在 db 中,它也会将其添加到 db 中。
有问题的代码片段如下(2 种方法)。 要发生错误,必须对这两种方法进行 3 处更改,如与 async await 语法相关的注释中
我想通过这些更改了解有关此异常的内部结构
方法一
public async Task<LocalizedString> GetResourceAsync(string resourceKey, CultureInfo culture, string location = "shared")
{
var tenant = ((MultiTenantContextAccessor<AppTenant>)_serviceProvider.GetRequiredService(typeof(IMultiTenantContextAccessor<AppTenant>))).MultiTenantContext.TenantInfo;
if (string.IsNullOrWhiteSpace(resourceKey))
throw new ArgumentNullException(nameof(resourceKey));
var cacheKey = $"{location}.{resourceKey}";
if (!_cacheProvider.TryGetValue(tenant.Id, cacheKey, culture, out var value))
{
using (var scope = _serviceProvider.GetScopedService(out T context))
{
var item = context
.Set<LocalizationResource>()
.SelectMany(r => r.Translations)
.Where(t => t.Resource.ResourceKey == resourceKey
&& t.Resource.Module == location
&& t.Language == culture.Name)
.Select(p => new
{
p.LocalizedValue
})
.SingleOrDefault();
//change 1) change above to use **await context** {...} **SingleOrDefaultAsync()**
if (item == null)
if (_settings.CreateMissingTranslationsIfNotFound)
await AddMissingResourceKeysAsync(resourceKey, location); //AddMissingResourceKeys(resourceKey);
value = item?.LocalizedValue ?? string.Empty;
if (string.IsNullOrWhiteSpace(value))
switch (_settings.FallBackBehavior)
{
case FallBackBehaviorEnum.KeyName:
value = resourceKey;
break;
case FallBackBehaviorEnum.DefaultCulture:
if (culture.Name != DefaultCulture.Name)
return await GetResourceAsync(resourceKey, DefaultCulture,location);
break;
}
}
_cacheProvider.Set(tenant.Id, cacheKey, culture, value);
}
return new LocalizedString(resourceKey, value!);
}
方法二
private async Task AddMissingResourceKeysAsync(string resourceKey, string location="shared")
////Change 2): remove async from method signature to
////private Task AddMissingResourceKeysAsync(string resourceKey, string location="shared")
{
var modificationDate = DateTime.UtcNow;
//resourceKey = $"{location}.{resourceKey}";
using var scope = _serviceProvider.GetScopedService(out T context);
var resource = context
.Set<LocalizationResource>()
.Include(t => t.Translations)
.SingleOrDefault(r => r.ResourceKey == resourceKey && r.Module==location);
if (resource == null)
{
resource = new LocalizationResource
{
Module = location,
ResourceKey = resourceKey,
//Modified = modificationDate,
Translations = new List<LocalizationResourceTranslation>()
};
context.Add(resource);
}
_requestLocalizationSettings.SupportedCultures.ToList()
.ForEach(culture =>
{
if (resource.Translations.All(t => t.Language != culture.Name))
{
//resource.Modified = modificationDate;
resource.Translations.Add(new LocalizationResourceTranslation
{
Language = culture.Name
//, Modified = modificationDate
});
}
});
await context.SaveChangesAsync();
////change 3) change above to return context.SaveChangesAsync();
}
完成以上 3 项更改后,它会始终抛出此异常,如果我删除这些更改,它会正常工作。
更改 #1 无关紧要,因为它只是修复了Misuse - Longrunning Operations Under async Methods :
我们注意到,即使在 .NET 或第三方库中有这些方法的相应异步版本,开发人员也会在异步方法下使用一些可能长时间运行的操作。
或者简单地说,在异步方法内部使用库异步方法(如果存在)。
更改#2 和#3 是不正确的。 看起来您正在尝试遵循Misuse - Unnecessary async Methods :
有一些异步方法不需要使用 async/await。 添加 async 修饰符是有代价的:编译器在每个异步方法中生成一些代码。
但是,您的方法需要async/await,因为它包含一个一次性 scope,它必须一直保存到作用域服务(在本例中为 db 上下文)的异步操作完成。 否则 scope 退出,数据库上下文被释放,没有人能说如果SaveChangesAsync
在释放时挂起会发生什么 - 你可能会得到有问题的错误,或者任何其他错误,这完全是意料之外的,并且行为是未定义的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.