在以下场景中如何将自定义对象映射到相应的任务?

How to map the custom object with corresponding Task in the following scenario?

我正在开发一个控制台应用程序,其中我有一个第三方休息客户端(准确地说是 wordpress 休息客户端),它有一些方法与 return Task 对象。一个示例方法签名可以是这样的:

public Task<bool> Delete(int id);

我有一个要删除的 Post 列表。我可以简单地做一些事情:

public void DeleteGivenPosts(List<Post> posts) {
  posts.ForEach(async post => await wpRestClient.Delete(post.Id));
}

在这种情况下,删除是即刻完成的。如果我记录有关删除的 post 的信息会更好。日志语句如:

logger.Log($"A post with {post.Id} is deleted");

所以,我决定计划任务。

public async Task DeleteGivenPosts(List<Post> posts) {
  var postDeletionTasks = posts.Select(post => wpRestClient.Delete(post.Id));

  foreach (var deletionTask in TaskExtensionUtil.GetTasksInCompletingOrder(postsDeletionTasks)) {
    bool deletionResult = await deletionTask;

    if(deletionResult) {
      //i want to log success of deletion here
    } else {
      //i want to log the failure of deletion here
    }
  }
}

这里 TaskExtensionUtil.GetTasksInCompletingOrder() 是一个辅助方法,它 return 按任务完成的顺序排列任务。代码是:

public static List<Task<T>> GetTasksInCompletingOrder<T>(IEnumerable<Task<T>> sourceTasks) {
  var sourceTasksArr = sourceTasks.ToArray();
  var taskCompletionSourceArr = new TaskCompletionSource<T>[sourceTasksArr.Length];

  var currentSlot = -1;
  for (int i = 0; i < sourceTasksArr.Length; i++) {
    taskCompletionSourceArr[i] = new TaskCompletionSource<T>();
    sourceTasksArr[i].ContinueWith(prev => {
      int indexToSet = Interlocked.Increment(ref currentSlot);
      taskCompletionSourceArr[indexToSet].SetResult(prev.Result);
    });
  }

  return taskCompletionSourceArr.Select(i => i.Task).ToList();
}

问题是 deletionResultbool。为了记录有关 post 被删除的信息,我需要获取与删除任务关联的 Post 对象。

我想创建一个字典,将删除任务映射到相应的 Post,方法如下:

posts.Select(post => new { deletionTask = wpRestClient.Delete(post.Id), post})
     .ToDictionary(i => i.deletionTask, i => i.post);

但这行不通,因为在 GetTasksInCompletingOrder 中,原始删除任务被转换为 TaskCompletionSource 任务。所以我总是会得到一个例外,即字典中不存在该键。此外,我不确定当字典以 Task 个对象作为键时它会如何表现。

此时我不知道如何实现日志记录。我将不胜感激任何帮助。

删除后如何写入logstatement?

public async Task DeleteGivenPosts(List<Post> posts)
{
    await Task.WhenAll(
    posts.Select(async post =>
    {
        bool res = await wpRestClient.Delete(post.Id);
        string message = res ? $"Post {post.Id} is deleted" : $"Post {post.Id} survived!";
        logger.Log(message);
    }));
}

这里有一个小的 LinqPad 程序来举例说明该方法的工作原理:

async void Main()
{
    List<Post> postList = Enumerable.Range(1, 12).Select(id => new Post {Id = id}).ToList();
    Console.WriteLine("Start Deletion");
    await DeleteGivenPosts(postList);
    Console.WriteLine("Finished Deletion");
}

public static MyRestClient wpRestClient = new MyRestClient();
// Define other methods and classes here
public async Task DeleteGivenPosts(List<Post> posts)
{
    await Task.WhenAll(
    posts.Select(async post =>
    {
        bool res = await wpRestClient.Delete(post.Id);
        string message = res ? $"Post {post.Id} is deleted" : $"Post {post.Id} survived!";
        Console.WriteLine(message);
    }));
}

public static Random rand = new Random(DateTime.Now.Millisecond);

public class MyRestClient
{
    public async Task<bool> Delete(int i)
    {
        return await Task<bool>.Run(() => { Thread.Sleep(400); return rand.Next(1,4) == 1;});
    }
}

public class Post
{
    public int Id { get; set; }
}

输出:

Start Deletion
Post 1 is deleted
Post 3 survived!
Post 5 survived!
Post 6 survived!
Post 2 survived!
Post 4 survived!
Post 8 survived!
Post 7 survived!
Post 9 survived!
Post 11 survived!
Post 10 is deleted
Post 12 is deleted
Finished Deletion

你也可以用你自己的方法包装它,像这样:

IEnumerable<Task<DeletionResult>> postDeletionTasks = posts.Select(post => DeletePost(post.Id));

您的方法和结果 class 可能类似于:

private async Task<DeletionResult> DeletePost(int postId)
{
    bool result = await wpRestClient.Delete(postId);
    return new DeletionResult(result, postId);
}

public class DeletionResult
{
    public DeletionResult(bool result, int postId)
    {
        Result = result;
        PostId = postId;
    }

    public bool Result { get; }
    public int PostId { get; }
}

这样你就会得到一个任务列表,结果包含它所影响的 PostId