如何访问调用异步方法时可用的变量?
How to access a variable that was available when an async method was called?
动物名称是从 API 中获取的,如果找不到该动物,可能会 return 404。但是为了正确记录错误,我们需要访问动物的国家。可能吗?我读过 something from a guy called Stephen Cleary 让我觉得 lambda 是可能的,但我找不到任何东西。
var gettingNames = new List<Task<string>>();
foreach (var animal in animals)
{
gettingNames.Add(this.zooApi.GetNameAsync(animal));
}
try
{
await Task.WhenAll(gettingNames);
}
catch (Exception e)
{
var exception = gettingNames.Where(task => task.IsFaulted)
.SelectMany(x => x.Exception.InnerExceptions).First();
this.logger.LogError("The animal name from {Country} was not found",
animal.Country); // This is the goal
}
解决此问题的一种方法是将每个 Animal
投影到一个 Task
,它包含比裸名或裸错误更多的信息。例如,您可以将其投影到包含三部分信息的 Task<ValueTuple<Animal, string, Exception>>
:动物、来自 zooApi
的动物学名以及调用 zooApi.GetScientificNameAsync
时可能发生的错误方法。
执行此投影的最简单方法是 LINQ Select
运算符:
List<Task<(Animal, string, Exception)>> tasks = animals.Select(async animal =>
{
try
{
return (animal, await this.zooApi.GetScientificNameAsync(animal),
(Exception)null);
}
catch (Exception ex)
{
return (animal, null, ex);
}
}).ToList();
(Animal, string, Exception)[] results = await Task.WhenAll(tasks);
foreach (var (animal, scientificName, error) in results)
{
if (error != null)
this.logger.LogError(error,
$"The {animal.Name} from {animal.Country} was not found");
}
你几乎成功了。 :)
您需要 Dictionary<Task<string>, string>
结构而不是 List<Task<string>>
:
static async Task Main()
{
var taskInputMapping = new Dictionary<Task<string>, string>();
var inputs = new[] { "input", "fault", "error", "test"};
foreach (var input in inputs)
{
taskInputMapping.Add(DelayEcho(input), input);
}
try
{
await Task.WhenAll(taskInputMapping.Keys);
}
catch
{
foreach (var pair in taskInputMapping.Where(t => t.Key.IsFaulted))
{
Console.WriteLine($"{pair.Value}: {pair.Key.Exception?.GetType().Name}");
}
}
}
static readonly ImmutableArray<string> wrongInputs =
ImmutableArray.Create("error", "fault");
static async Task<string> DelayEcho(string input)
{
if (wrongInputs.Contains(input)) throw new ArgumentException();
await Task.Delay(10);
return input;
}
taskInputMapping.Add(DelayEcho(input), input)
:将输入存储在任务本身旁边
taskInputMapping.Where(t => t.Key.IsFaulted)
:遍历出错的任务
$"{pair.Value}: {pair.Key.Exception?.GetType().Name}"
:检索输入+相关错误
我综合了这些答案得出了这个:
var tasks = animals.Select(async animal =>
{
try
{
return await this.zooApi.GetNameAsync(animal);
}
catch (Exception ex)
{
this.logger.LogError(error,
$"The {animal.Name} from {animal.Country} was not found");
return null;
}
});
var results = await Task.WhenAll(tasks);
foreach (var name in results.Where(x => x != null))...
动物名称是从 API 中获取的,如果找不到该动物,可能会 return 404。但是为了正确记录错误,我们需要访问动物的国家。可能吗?我读过 something from a guy called Stephen Cleary 让我觉得 lambda 是可能的,但我找不到任何东西。
var gettingNames = new List<Task<string>>();
foreach (var animal in animals)
{
gettingNames.Add(this.zooApi.GetNameAsync(animal));
}
try
{
await Task.WhenAll(gettingNames);
}
catch (Exception e)
{
var exception = gettingNames.Where(task => task.IsFaulted)
.SelectMany(x => x.Exception.InnerExceptions).First();
this.logger.LogError("The animal name from {Country} was not found",
animal.Country); // This is the goal
}
解决此问题的一种方法是将每个 Animal
投影到一个 Task
,它包含比裸名或裸错误更多的信息。例如,您可以将其投影到包含三部分信息的 Task<ValueTuple<Animal, string, Exception>>
:动物、来自 zooApi
的动物学名以及调用 zooApi.GetScientificNameAsync
时可能发生的错误方法。
执行此投影的最简单方法是 LINQ Select
运算符:
List<Task<(Animal, string, Exception)>> tasks = animals.Select(async animal =>
{
try
{
return (animal, await this.zooApi.GetScientificNameAsync(animal),
(Exception)null);
}
catch (Exception ex)
{
return (animal, null, ex);
}
}).ToList();
(Animal, string, Exception)[] results = await Task.WhenAll(tasks);
foreach (var (animal, scientificName, error) in results)
{
if (error != null)
this.logger.LogError(error,
$"The {animal.Name} from {animal.Country} was not found");
}
你几乎成功了。 :)
您需要 Dictionary<Task<string>, string>
结构而不是 List<Task<string>>
:
static async Task Main()
{
var taskInputMapping = new Dictionary<Task<string>, string>();
var inputs = new[] { "input", "fault", "error", "test"};
foreach (var input in inputs)
{
taskInputMapping.Add(DelayEcho(input), input);
}
try
{
await Task.WhenAll(taskInputMapping.Keys);
}
catch
{
foreach (var pair in taskInputMapping.Where(t => t.Key.IsFaulted))
{
Console.WriteLine($"{pair.Value}: {pair.Key.Exception?.GetType().Name}");
}
}
}
static readonly ImmutableArray<string> wrongInputs =
ImmutableArray.Create("error", "fault");
static async Task<string> DelayEcho(string input)
{
if (wrongInputs.Contains(input)) throw new ArgumentException();
await Task.Delay(10);
return input;
}
taskInputMapping.Add(DelayEcho(input), input)
:将输入存储在任务本身旁边taskInputMapping.Where(t => t.Key.IsFaulted)
:遍历出错的任务$"{pair.Value}: {pair.Key.Exception?.GetType().Name}"
:检索输入+相关错误
我综合了这些答案得出了这个:
var tasks = animals.Select(async animal =>
{
try
{
return await this.zooApi.GetNameAsync(animal);
}
catch (Exception ex)
{
this.logger.LogError(error,
$"The {animal.Name} from {animal.Country} was not found");
return null;
}
});
var results = await Task.WhenAll(tasks);
foreach (var name in results.Where(x => x != null))...