dotnet icon indicating copy to clipboard operation
dotnet copied to clipboard

IMessenger.Send behaviour

Open viktor-vintertass opened this issue 1 year ago • 1 comments

Describe the bug

Service registration: AddSingleton<IMessenger>(WeakReferenceMessenger.Default)

Message registration: _messenger.Register<AsyncRelayCommandPageViewModel, TestAsyncMessage1>(this, static (r, m) => m.Reply(r.Action1(m)));

Behavour:

Case1: If Action1 is of type Task<T> and we do following:

string response = await _messenger.Send<TestAsyncMessage1>(new());

private async Task<string> Action1(TestAsyncMessage1 msg)
{
    await Task.Delay(TimeSpan.FromSeconds(5));
    Trace.WriteLine("Action1 process complete");
    return "hello";
}

the response is of type string and contains the value hello.

Case2: However, if Action1 is of type Task and we do following:

Task response = await _messenger.Send<TestAsyncMessage1>(new());

private async Task Action1(TestAsyncMessage1 msg)
{
    await Task.Delay(TimeSpan.FromSeconds(5));
    Trace.WriteLine("Action1 process complete");
}

the response is of type Task.

Issue: In Case1 does the await actually awaits the internal task (Action1) and return the value of the task, in this case the string hello. In Case2 does the await awaits that the message is sent, but not the internal task (Action1).

So if we want the same behaviour for Case2, where we want the internal task to be completed, we have to do something like:

Task response = await _messenger.Send<TestAsyncMessage1>(new());
await response;

We are now implementation our own solution, using an extension method:

public static class ExtensionMethods
{
    public static async Task ProcessTask<TMessage>(this IMessenger messenger, TMessage message)
        where TMessage : AsyncRequestMessage<Task>
        {
            var task = await messenger.Send(message);
            await task;
        }

    public static Task ProcessTask<TMessage>(this IMessenger messenger)
        where TMessage : AsyncRequestMessage<Task>, new()
        => messenger.ProcessTask(new TMessage());
}

Making it possible to do following:

await _messenger.ProcessTask<TestAsyncMessage1>(new());

Description Depening on the return type, we have different behavour. Is this wanted? Would it make sense to add something like ProcessTask to the library to extend the functionality?

Regression

No response

Steps to reproduce

Microsoft Visual Studio Professional 2022 (64-bit) - Current Version 17.12.4

CommunityToolkit.Mvvm Version 8.2.2

Expected behavior

See description of bug.

Screenshots

No response

IDE and version

VS 2022

IDE version

Version 17.12.4

Nuget packages

  • [ ] CommunityToolkit.Common
  • [ ] CommunityToolkit.Diagnostics
  • [ ] CommunityToolkit.HighPerformance
  • [x] CommunityToolkit.Mvvm (aka MVVM Toolkit)

Nuget package version(s)

8.2.2

Additional context

No response

Help us help you

Yes, I'd like to be assigned to work on this item

viktor-vintertass avatar Jan 17 '25 08:01 viktor-vintertass

After more investigation, we have decided that we are using async messages the wrong way. However, we still belive that the difference in behavior when a Task vs Task<string> are the return type should be investigated or clarified.

viktor-vintertass avatar Jan 23 '25 12:01 viktor-vintertass