Using Mediatr inside Unity game engine

33 Views Asked by At

I use Mediatr at work so I thought I'd give it a try inside Unity Game Engine just because I find it interesting to play around with different ways to do things. I was able to import the library just fine and working with ChatGPT on how to set it up The code below shows my setup.

RegisterHandlers() looks like it finds my 1 handler just fine. The code gets to UnityMediator.Send() and that's where it fails.

InvalidOperationException: No service for type 'System.Collections.Generic.IEnumerable1[MediatR.IPipelineBehavior2[MyRequest,System.String]]' has been registered. Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService (System.IServiceProvider provider, System.Type serviceType) (at :0) Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T] (System.IServiceProvider provider) (at :0) Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetServices[T] (System.IServiceProvider provider) (at :0) MediatR.Wrappers.RequestHandlerWrapperImpl2[TRequest,TResponse].Handle (MediatR.IRequest1[TResponse] request, System.IServiceProvider serviceProvider, System.Threading.CancellationToken cancellationToken) (at <583b45eab0d44acc83a8a1c7533fae86>:0) MediatR.Mediator.Send[TResponse] (MediatR.IRequest1[TResponse] request, System.Threading.CancellationToken cancellationToken) (at <583b45eab0d44acc83a8a1c7533fae86>:0) UnityMediator.Send[TResponse] (MediatR.IRequest1[TResponse] request, System.Threading.CancellationToken cancellationToken) (at Assets/Scripts/UnityMediatr.cs:65) MyScript.Start () (at Assets/Scripts/MyScript.cs:16)

Something I also noticed just now is after I make a change and go back to Unity and it compiles I get this error but the code runs if I run the project.

Failed to find entry-points: System.Exception: Unexpected exception while collecting types in assembly Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null ---> Mono.Cecil.AssemblyResolutionException: Failed to resolve assembly: 'MediatR.Contracts, Version=2.0.1.0, Culture=neutral, PublicKeyToken=bb9a41a5e8aaa7e2' at Mono.Cecil.BaseAssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name, Mono.Cecil.ReaderParameters parameters) [0x00105] in :0 at zzzUnity.Burst.CodeGen.AssemblyResolver.Resolve (Mono.Cecil.AssemblyNameReference name) [0x00039] in :0 at Burst.Compiler.IL.AssemblyLoader.Resolve (Mono.Cecil.AssemblyNameReference name) [0x00079] in :0 at Mono.Cecil.MetadataResolver.Resolve (Mono.Cecil.TypeReference type) [0x00038] in :0 at Mono.Cecil.ModuleDefinition.Resolve (Mono.Cecil.TypeReference type) [0x00006] in :0 at Mono.Cecil.TypeReference.Resolve () [0x00006] in :0 at Burst.Compiler.IL.Server.EntryPointMethodFinder.CollectGenericTypeInstances (Mono.Cecil.TypeReference type, System.Collections.Generic.List1[T] types, System.Collections.Generic.HashSet1[T] visited) [0x0002f] in :0 at Burst.Compiler.IL.Server.EntryPointMethodFinder.CollectGenericTypeInstances (Mono.Cecil.AssemblyDefinition assembly, System.Collections.Generic.List1[T] types, System.Collections.Generic.HashSet1[T] visited) [0x00057] in :0 at Burst.Compiler.IL.Server.EntryPointMethodFinder.FindEntryPoints (System.String[] rootAssemblyNames, Burst.Compiler.IL.Hashing.CacheRuntime.HashCacheAssemblyStore assemblyStore, Burst.Compiler.IL.AssemblyLoader assemblyLoader, Burst.Compiler.IL.NativeCompilerOptions options, Burst.Compiler.IL.Server.ProfileDelegate profileCallback, System.Boolean includeRootAssemblyReferences, System.Boolean splitTargets, Burst.Compiler.IL.Helpers.DebugLogWriter debugWriter) [0x0019d] in :0 --- End of inner exception stack trace --- at Burst.Compiler.IL.Server.EntryPointMethodFinder.FindEntryPoints (System.String[] rootAssemblyNames, Burst.Compiler.IL.Hashing.CacheRuntime.HashCacheAssemblyStore assemblyStore, Burst.Compiler.IL.AssemblyLoader assemblyLoader, Burst.Compiler.IL.NativeCompilerOptions options, Burst.Compiler.IL.Server.ProfileDelegate profileCallback, System.Boolean includeRootAssemblyReferences, System.Boolean splitTargets, Burst.Compiler.IL.Helpers.DebugLogWriter debugWriter) [0x001d9] in :0 at Burst.Compiler.IL.Server.FindMethodsJob.Execute (Burst.Compiler.IL.Server.CompilerServerJobExecutionContext context) [0x00133] in :0

UnityMediator.cs

public class CustomServiceProvider : IServiceProvider
{
    private readonly ServiceFactory _serviceFactory;

    public CustomServiceProvider(ServiceFactory serviceFactory)
    {
        _serviceFactory = serviceFactory ?? throw new ArgumentNullException(nameof(serviceFactory));
    }

    public object GetService(Type serviceType)
    {
        return _serviceFactory.CreateInstance(serviceType);
    }
}

public class UnityMediator : IMediator
{
    private readonly IMediator _mediator;

    public UnityMediator()
    {
        var assembly = typeof(UnityMediator).Assembly;
        RegisterHandlers(assembly);

        var serviceFactory = new ServiceFactory(type => {
            var t = type;

            return Type.GetType($"{assembly.GetName().Name}.{type.Name}"); 
        });
        var serviceProvider = new CustomServiceProvider(serviceFactory);
        _mediator = new Mediator(serviceProvider);
    }

    public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken cancellationToken = default)
    {
        return _mediator.Send(request, cancellationToken);
    }

    public Task Send<TRequest>(TRequest request, CancellationToken cancellationToken = default) where TRequest : IRequest
    {
        return _mediator.Send(request, cancellationToken);
    }

    public Task<object> Send(object request, CancellationToken cancellationToken = default)
    {
        return _mediator.Send(request, cancellationToken);
    }

    private void RegisterHandlers(System.Reflection.Assembly assembly)
    {
        var handlerTypes = assembly.GetTypes()
            .Where(t => t.GetInterfaces().Any(i =>
                i.IsGenericType &&
                (i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>) || i.GetGenericTypeDefinition() == typeof(INotificationHandler<>))
            ));

        foreach (var handlerType in handlerTypes)
        {
            var interfaces = handlerType.GetInterfaces()
                .Where(i => i.IsGenericType &&
                            (i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>) || i.GetGenericTypeDefinition() == typeof(INotificationHandler<>)))
                .ToList();

            foreach (var @interface in interfaces)
            {
                var requestType = @interface.GetGenericArguments()[0];
                var handlerInstance = Activator.CreateInstance(handlerType);
                ServiceLocator.RegisterService(@interface, handlerInstance);
            }
        }
    }
}

ServiceFactory.cs

public class ServiceFactory
{
    private readonly Func<Type, object> _createInstance;

    public ServiceFactory(Func<Type, object> createInstance)
    {
        _createInstance = createInstance ?? throw new ArgumentNullException(nameof(createInstance));
    }

    public object CreateInstance(Type type)
    {
        return _createInstance(type);
    }
}

ServiceLocator.cs

public static class ServiceLocator
{
    private static Dictionary<Type, object> services = new Dictionary<Type, object>();

    public static void RegisterService(Type serviceType, object service)
    {
        services[serviceType] = service;
    }

    public static T GetService<T>()
    {
        Type type = typeof(T);
        if (services.ContainsKey(type))
        {
            return (T)services[type];
        }
        else
        {
            Debug.LogError($"Service of type {type} not found.");
            return default(T);
        }
    }
}

MyScript.cs (test script)

public class MyScript : MonoBehaviour
{
    private IMediator _mediator;

    void Start()
    {
        // Assuming you have registered UnityMediator with a service locator
        _mediator = ServiceLocator.GetService<IMediator>();

        // Example of sending a request and handling the response
        var response = _mediator.Send(new MyRequest("Test"));
        Debug.Log("Response: " + response.Result);
    }
}

public class MyRequest : IRequest<string>
{
    public string Input { get; }

    public MyRequest(string input)
    {
        Input = input;
    }
}

public class MyRequestHandler : IRequestHandler<MyRequest, string>
{
    public Task<string> Handle(MyRequest request, CancellationToken cancellationToken)
    {
        // Handle the request logic here
        string result = $"Received input: {request.Input}";
        return Task.FromResult(result);
    }
}

GameManager.cs

public class GameManager : MonoBehaviour
{
    private UnityMediator _mediator;

    void Awake()
    {
        _mediator = new UnityMediator();
        ServiceLocator.RegisterService(typeof(IMediator), _mediator);
    }
}
0

There are 0 best solutions below