.NET MAUI - MediatR 을 이용한 Event Mechanism 구현

2022. 9. 1. 00:00MAUI

반응형

.NET MAUI 에서 view 와 view 간의 data 전달을  위해서  Messenger 를 이용한다. 

MVVM 에서 특히 이 Messenger 를 이용한 Subscribe and Publish pattern 은 일반화된 데이터 전달 방식이다.

그런데 MAUI 에서도 Service 계층이 있을 수 있고 이 Service 계층은 view viewmodel 과는 다른 layer 에 있다고 생각해 보자. (다른 project 일 수도 있고 project 내에서 폴더로 구분하여 다른 폴더에 구현 했을수 도 있다.)

이럴경우는 CommunityToolkit.MVVM 에서 지원하는 Messager 는 사용하지 못할 수 있다. 

 

이럴때를 이용하여 MediatR 이라는 것을 활용할 수 있다. 

MediatR 은 Mediator 에 대한 dotnet 구현체 이다. 

(과학자 분들이 MediatR은 mediator pattern 이 아니다라고 할텐데 .. 맞다 당신들 말이 전적으로 다 맞다)

 

Mediator 의 class diagram

 

어쨌든 이 MediatR 을 이용해 우리는 app 에서 viewmodel 과 다른 handler 또는 service 간에 data 를 전달 하겠다.

 

일단 MediatR Nuget 를 통해 설치 하자 

dotnet add package MediatR.Extensions.Microsoft.DependencyInjection

 

그런다음 MauiProgram.cs 로 이동하여 MediatR 을 Maui 에서 사용가능하도록 등록 하자!!

...
using MediatR;
...

...
builder.Services.AddMediatR(Assembly.GetExecutingAssembly());
...

 

일단 message 를 전달하기 위해 GetUserCommand 라는 Command 를 만들었다.

return 값이 있고 string 형태 이므로 IRequest<string> 형태를 Implements 한다.

Request/GetUserCommand.cs

using MediatR;

namespace MauiApp1.Request;
public class GetUserCommand : IRequest<string>
{
}

 

GetUserCommand 를 MediatR 에서 Send 하게 되면 IRequestHandler<GetUserCommand, string> 을 구현한 UserService 에서 처리하게 된다. 여기서는 httpClient 를 통해 mockaroo 라는 mock site 로 부터 json data 를 받고 있다.

 

Services/UserService.cs

using MauiApp1.Request;
using MediatR;

namespace MauiApp1.Services;

public class UserService : IRequestHandler<GetUserCommand, string>
{

    public async Task<string> Handle(GetUserCommand request, CancellationToken cancellationToken)
    {
        using HttpClient httpClient = new HttpClient();
        var response = await httpClient
            .GetAsync("https://my.api.mockaroo.com/users.json?key=8d630ff0");

        return await response.Content.ReadAsStringAsync();
    }
}

public class User
{
    public int id { get; set; }
    public string first_name { get; set; }
    public string last_name { get; set; }
    public string email { get; set; }
    public string gender { get; set; }
    public string ip_address { get; set; }
}

 

EventMechanism/MainPage.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="MauiApp1.EventMechanism.MainPage"
             xmlns:local="clr-namespace:MauiApp1.EventMechanism"
             x:DataType="local:MainViewModel"
             Title="MediatR Test">
    <VerticalStackLayout>
        <Label Text="{Binding Message, Mode=OneWay}"/>
        <Button
            Text="Change message"
            Command="{Binding CallAPICommand}"/>

        <CollectionView ItemsSource="{Binding UserModels}">
            <CollectionView.ItemTemplate>
                <DataTemplate x:DataType="local:UserModel">
                    <Grid ColumnDefinitions="*,2*,2*,4*,*">
                        <Label Text="{Binding Id}" FontAttributes="Bold" />
                        <Label Grid.Column="1" Text="{Binding FirstName}" />
                        <Label Grid.Column="2" Text="{Binding LastName}" FontSize="12"/>
                        <Label Grid.Column="3" Text="{Binding Email}" FontSize="10"/>
                        <Label Grid.Column="4" Text="{Binding Gender}" FontSize="10"/>
                        <!--<Label Grid.Column="5" Text="{Binding IpAddress}" />-->
                    </Grid>
                </DataTemplate>
            </CollectionView.ItemTemplate>
        </CollectionView>
    </VerticalStackLayout>
</ContentPage>

 

EventMechanism/MainPage.xaml.cs

using MauiApp1.InjectableServices;

namespace MauiApp1.EventMechanism;
public partial class MainPage : ContentPage, ITransientService
{
	public MainPage(MainViewModel viewModel)
	{
		InitializeComponent();
		BindingContext = viewModel;
	}
}

 

viewModel에서는 IMediator 을 주입 받아서 CallApiCommand 가 실행되면 _mediator.send 를 통해 message 를 전달하고 Message 에는 UserService 로 부터 return 받는 string을 받게된다.

EventMechanism/MainViewModel.cs

using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using MauiApp1.InjectableServices;
using MauiApp1.Request;
using MauiApp1.Services;
using MediatR;
using System.Collections.ObjectModel;

namespace MauiApp1.EventMechanism;
public partial class MainViewModel : ObservableObject, ITransientService
{
    public MainViewModel(IMediator mediator)
    {
        UserModels ??= new ObservableCollection<UserModel>();
        _mediator = mediator;
    }

    [ObservableProperty]
    private int _counter;

    [ObservableProperty]
    private string _message;

    private readonly IMediator _mediator;

    [RelayCommand]
    private void IncrementCounter() => Counter++;

    [RelayCommand]
    private async void CallAPI()
    {
        Message = await _mediator.Send(new GetUserCommand());


        var users = System.Text.Json.JsonSerializer.Deserialize<List<User>>(Message);

        foreach (var user in users)
        {
            UserModels.Add(new UserModel
            {
                id = user.id,
                firstName = user.first_name,
                lastName = user.last_name,
                email = user.email,
                gender = user.gender,
                ipAddress = user.ip_address,
            });
        }
    }

    public ObservableCollection<UserModel> UserModels
    {
        get;
        set;
    }
}

public partial class UserModel : ObservableObject
{
    [ObservableProperty]
    public int id;
    [ObservableProperty]
    public string firstName;
    [ObservableProperty]
    public string lastName;
    [ObservableProperty]
    public string email;
    [ObservableProperty]
    public string gender;
    [ObservableProperty]
    public string ipAddress;
}

 

관련영상

https://youtu.be/MPtv6Kvp2Ag

반응형