我有以下用例FetchDataService
(作为作用域注入)正在使用 httpClient 从 API 获取数据。
然后服务分析数据并将其运行await SaveAsync
到数据库中。
到目前为止,一切都很好。
然后我添加了另一个服务:(AddressEnrichmentService
作为作用域注入)在“分析”阶段异步调用外部服务并带来更多数据。
这项服务正在搞乱整个应用程序。我从指示一些线程问题的数据库处理程序 (NHibernate) 中得到随机异常。我真的不能把我的手指放在它上面。几个不同的随机异常......运行时会抛出这些异常await SaveAsync
。(我会在最后添加它们)。
public async Task Fetch(string url, UserModel userModel ){
string res = null;
using (var httpClient = new HttpClient()){
res = await httpClient.GetStringAsync(url);
}
await databaseLogRepository.SaveAsync(new Log{Message = "The data is here..." + res});
var profile = JsonConvert.DeserializeObject<ProfileModel >(res);
profile.Data.Locations.ForEach(async x =>
{
var address = await addressEnrichmentService.EnrichAsync(x.StreetAddress + " " + x.Locality + " " + x.Country);
userModel.Address = address;
await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
});
}
有问题的服务:
public class AddressEnrichmentService : IAddressEnrichmentService
{
public async Task<AddressModel> EnrichAsync(string address)
{
string res = null;
using (HttpClient client = new HttpClient())
{
var url = "https://maps.googleapis.com/maps/api/geocode/json?key=__KEY__&address=" + HttpUtility.UrlEncode(address);
res = await client.GetStringAsync(url);// probably the problematic row
}
return JsonConvert.DeserializeObject<AddressModel>(res);
}
}
用法
UserModel userModel = new UserModel();
await fetchDataService.Fetch(url, userModel);
await userRepo.SaveAsync(userModel);
又addressEnrichmentService.EnrichAsync
是把一切都搞砸了。我怎么知道EnrichAsync
是不是搞砸了?
如果我转换res = await client.GetStringAsync(url);
为res = client.GetStringAsync(url).GetAwaiter().GetResult();
我不会收到任何错误。100 次尝试 100 次我没有错误。如果我回滚它,res = await client.GetStringAsync(url);
我每次都会出错。
关于异常,再次,我得到一些与并发相关的错误的迹象,这是保存方法:
public async Task<T> SaveAsync(T entity)
{
using (var tr = session.BeginTransaction())
{
//session is injected as scoped
await session.SaveOrUpdateAsync(entity);
tr.Commit();
}
return entity;
}
MySqlException: There is already an open DataReader associated with this Connection which must be closed first.
NHibernate.HibernateException: 'Flush during cascade is dangerous'
这是问题所在:
profile.Data.Locations.ForEach(async x =>
该ForEach
方法接受一个Action
,而不是一个异步委托。因此,您的async
委托 (the Task
)的返回值将被忽略。结果是你最终得到了一种async void
方法,它在你之前折磨了无数开发人员(搜索async void
以查看永无止境的相关问题列表)。由于Task
不等待 s,它们同时运行。多个线程正在向您的数据库发送并发请求,这显然无法处理并行请求。更不用说这些任务不能再被观察到了。它们已成为一劳永逸的任务。
为了解决这个问题ForEach
,用普通香草代替花式foreach
。每个都Task
将得到适当的等待,不会发生不需要的并行性,问题将得到解决。
foreach (var x in profile.Data.Locations)
{
var address = await addressEnrichmentService.EnrichAsync(
x.StreetAddress + " " + x.Locality + " " + x.Country);
userModel.Address = address;
await databaseLogRepository.SaveAsync(new Log{Message = "enriched address"});
}
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句