Я думаю, что ваша основная проблема связана с вашим базовым классом ActionBase
.
Метод Action
в этом классе - это, по сути, оболочка, которая выполняет некоторую общую логику перед вызовом фактического действия.
Общеизвестно, что для работы требуются некоторые внешние службы (здесь IEmailer
), зависимость которых вы не хотите навязывать производным классам.
Возможно, более простым подходом к использованию событий было бы сделать открытое свойство IEmailer
и установить его при создании действия в методе EventTriggerActionService.Execute
.
Это избавит вас от необходимости передавать ссылку через конструктор, но при этом иметь возможность доступа к IEmailer instance
.
Вы бы получили что-то вроде:
public abstract class ActionBase
{
protected IEmailer Emailer { get; set; }
protected Dictionary<string,string> Arguments { get; set; }
public void Execute()
{
// Do something with Emailer.
ExecuteAction(); // Execute the custom action
}
public abstract ResultBase ExecuteAction();
}
public class EventTriggerActionService
{
// omit for readability
public void Execute(EventTriggerAction eventTriggerAction, EventContext context)
{
// omit for readability
var action = (ActionBase)Activator.CreateInstance(); // Instantiate the action by its base class, ActionBase
action.Arguments = data.Arguments; // This is a dictionary that contains the arguments of the action
action.Emailer = _emailerFactory.Create();
// omit for readability
}
// omit for readability
}
Однако, поскольку у вас есть контроль над тем, как выполняются действия, зачем вводить общую логику в базовый класс?
Вы можете легко извлечь общую логику и отделить ее от действия.
Например:
public interface IActionBase
{
ResultBase ExecuteAction();
}
public class SimpleEmailSender : IActionBase
{
private Dictionary<string, string> arguments;
private IEmailer Emailer;
public SimpleEmailSender(Dictionary<string, string> arguments, IEmailer emailer)
{
this.arguments = arguments;
this.emailer = emailer;
}
public override ResultBase ExecuteAction()
{
this.emailer.Send(Arguments["EmailBody"]);
return null;
}
}
public class EventTriggerActionService
{
// omit for readability
public void Execute(EventTriggerAction eventTriggerAction, EventContext context)
{
// omit for readability
this.RunPreLogic();
action.ExecuteAction();
this.RunPostLogic();
// omit for readability
}
public void RunPreLogic() {}
public void RunPostLogic() {}
// omit for readability
}
Как видно из примера, логика удалена из базового класса. Освобождение любого производного класса от необходимости предоставлять эти услуги.
Осталось только внедрить необходимые сервисы в конструктор созданного класса.
Контейнеры DI, которые я использовал в прошлом, всегда обеспечивали какой-либо способ внедрения сервисов. Если вы не можете (или не хотите) использовать контейнер, не должно быть слишком сложно извлечь информацию конструктора и решить ее самостоятельно.
Если вы используете ядро ASP.NET, по умолчанию для внедрения зависимостей Microsoft предоставляется служебный класс ActivatorUtilities
для этого ( msdn )
Если вам не нужна ваша общая логика в EventTriggerActionService
, вы всегда можете извлечь ее в отдельный сервис или выполнить специальное действие, которое обрабатывает общую логику.