I have RegisterMessageHandlers that register message handler per message name.
I want to automatically find and register all handlers through reflection. I can annotate each message with MessageAttribute (like shown below) and get the message name through reflection as well. The problem is when I want to instantiate a handler via Activator I have to provide all the dependencies in the constructor.
My solution is to register all instances with DI and pass IServiceProvider to MainManagerClass(IServiceProvider provider) and then use ActivatorUtilities.CreateInstance(handlerType) to instantiate each discovered handler through reflection.
But, then I read that there is DI and service locator which is antipattern and it's not very clear when one becomes the other.
So, I think I need a DI in order to accomplish what I want. Or do I?
public class MainManagerClass
{
private Dictionary<string, MessageHandler> _handlers;
private void RegisterMessageHandlers()
{
_messageHandlers["msg1"] = new MessageHandler1(new Service1());
_messageHandlers["msg2"] = new MessageHandler2(new Service1(), new Service2());
}
}
public class MessageHandler1 : MessageHandler<Message1>
{
public MessageHandler1(IService1 service){}
}
public class MessageHandler2 : MessageHandler<Message2>
{
public MessageHandler1(IService1 service1, IService2 service2){}
}
public abstract class MessageHandler<T> : MessageHandler
{
}
public abstract class MessageHandler
{
}
[Message("msg1")]
public class Message1
{
}
UPDATE
public class MainManagerClass
{
private Dictionary<string, MessageHandler> _handlers;
private readonly IServiceProvider _serviceProvider;
public MainManagerClass(IServiceProvider serviceProvider)
{
}
private void RegisterMessageHandlers()
{
var messageHandlers = Assembly.GetCallingAssembly()
.GetTypes()
.Where(t => t.IsClass && !t.IsAbstract && t.IsSubclassOf(typeof(MessageHandler))).ToList();
foreach (var handler in messageHandlers)
{
var messageType = handler.BaseType.GenericTypeArguments[0];
var msgAttribute = messageType.CustomAttributes
.Where(t => t.AttributeType == typeof(MessageAttribute))
.FirstOrDefault();
if (msgAttribute == null)
throw new Exception($"Message name not defined for message type {messageType.Name}");
var msgName = msgAttribute.ConstructorArguments[0].Value.ToString();
_messageHandlers[msgName] = ActivatorUtilities.CreateInstance(_serviceProvider, handler) as MessageHandler;
}
}
}
internal class Program
{
static async Task Main(string[] args)
{
var host = CreateHostBuilder().Build();
var manager = new MainManagerClass(host.Services);
...
}
private static IHostBuilder CreateHostBuilder()
{
return Host.CreateDefaultBuilder()
.ConfigureServices((_, services) =>
{
services
.AddSingleton<IService1, Service1>()
.AddSingleton<IService2, Service2>()
.AddSingleton<IService3, Service3>()
;
});
}
}