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.IEnumerable
1[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);
}
}