Есть несколько способов зарегистрировать ваши обработчики. Я всегда начинаю с настройки всего вручную. В вашем примере это будет выглядеть так:
<!-- language: c# -->
container.Register<IMessageHandler<FooMessage>, FooHandler>();
container.Register<IMessageHandler<BarMessage>, BarHandler>();
// etc
Совет : Вместо использования абстрактного базового класса определите интерфейс IMessageHandler<T>
.
Как только приложение растет, это может стать громоздким, поэтому в вашем случае стоит начать регистрацию партии. Все контейнеры DI имеют разные механизмы для этого. Большинство контейнеров поддерживают это из коробки, а другие - нет. Вот как это сделать, используя Simple Injector :
<!-- language: c# -->
container.RegisterManyOpenGeneric(typeof(MessageHandler<>),
typeof(MessageHandler<>).Assembly);
Это зарегистрирует все конкретные типы, которые реализуют MessageHandler<T>
и находятся в той же сборке, что и MessageHandler<T>
. Опять же, это зависит от вашего выбора контейнера, как написать это.
Для обработки входящих произвольных сообщений вам понадобится немного размышлений, но вы не должны делать это в самом приложении. Просто определите хорошую абстракцию в приложении и перенесите использование отражения в ту часть приложения, где у вас есть конфигурация DI.
Например, вы можете определить интерфейс IMessageProcessor
, который позволяет обрабатывать любой тип Message
:
<!-- language: c# -->
public interface IMessageProcessor
{
void Process(Message message);
}
Рядом с вашей конфигурацией DI вы можете определить реализацию IMessageProcessor
, и она может выглядеть следующим образом:
<!-- language: c# -->
private class DIMessageProcessor : IMessageProcessor
{
private readonly Container container;
public DIMessageProcessor(Container container)
{
this.container = container;
}
public void Process(Message message)
{
if (message == null) throw new ArgumentNullException("message");
Type messageType = message.GetType();
Type handlerType =
typeof(IMessageHandler<>).MakeGenericType(messageType);
var handler = this.container.GetInstance(handlerType);
handlerType.GetMethod("Handle").Invoke(handler, message);
}
}
Важно оставить это вне приложения. Мало того, что вы будете смешивать обязанности, но будет сложнее добавить новое поведение. Подумайте, например, о предотвращении повторов сообщений (когда сообщение выполняется несколько раз). Было бы легко реализовать это поведение в качестве декоратора IMessageProcessor
. Кроме того, DIMessageProcessor
будет легко выполнить модульное тестирование или будет заменено новой, более производительной реализацией (и снова протестировано)
Есть несколько способов зарегистрировать DIMessageProcessor
, в зависимости от выбранного вами контейнера. Использование простого инжектора, например:
<!-- language: c# -->
container.RegisterSingle<IMessageProcessor>(new DIMessageProcessor(container));
С этим вы можете ввести IMessageProcessor
в типы, которые в этом нуждаются. Вот пример для WCF:
<!-- language: c# -->
[ServiceKnownType("GetKnownMessageTypes")]
public class WCFMessageService
{
private readonly IMessageProcessor processor;
public WCFMessageService()
{
this.processor =
Global.Container.GetInstance<IMessageProcessor>();
}
[OperationContract]
public void Process(Message message)
{
this.processor.Process(message);
}
public static IEnumerable<Type> GetKnownMessageTypes(
ICustomAttributeProvider provider)
{
var knownMessageTypes =
from type in typeof(Message).Assembly.GetTypes()
where typeof(Message).IsAssignableFrom(type)
select type;
return knownMessageTypes.ToArray();
}
}
Надеюсь, это поможет.