mercredi 9 mai 2018

"On Demand" dependency injection for dynamically instantiated classes

In my application, I have a class called EventTriggerActionService, which is responsible for instantiating custom actions that reside in different assemblies. It uses the class name and assembly name to load the module via Reflection, and executes it.

My main problem is: How do I inject the required dependencies into these custom actions? My actions all derive from a base class called ActionBase.

My initial solution was to first supply all the dependencies via constructor injection by just adding them as parameters in the (ActionBase)Activator.CreateInstance();, but this was problematic, because I didn't want to force all of my derived actions to take in the dependencies of other actions.

Here is my second solution: I decided to use events to put "Dependency Providers" in EventTriggerActionService. Using this approach, all of the dependencies for all custom actions will be injected in EventTriggerActionService, and the custom action will request the dependency by firing the event.

Here is my EventTriggerActionService:

public class EventTriggerActionService
{
    private IEmailerFactory _emailerFactory;
    public EventTriggerActionService(IEmailerFactory emailerFactory) 
    {
        _emailerFactory = emailerFactory;
    }

    public void Execute(EventTriggerAction eventTriggerAction, EventContext context)
    {   
        var assemblyName = eventTriggerAction.EventAction.EventActionHandlerAssembly;
        var className = eventTriggerAction.EventAction.EventActionHandlerClass;

        Assembly actionHandlerAssembly = Assembly.Load(assemblyName);
        Type actionHandlerType = actionHandlerAssembly.GetType(className);

        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

        // Register all the dependency providers here
        GetEmailer emailerHandler = GetEmailer;
        actionHandlerType.GetEvent("GetEmailer").AddEventHandler(action, emailerHandler); // Register the event handler

        action.Execute(); // Execute the Action
        return executionStatus;
    }

    // The custom action "Requests" a dependency by firing this event
    private void GetEmailer(ref IEmailer emailer)
    {
        emailer = _emailerFactory.Create();
    }
}

This is ActionBase:

public abstract class ActionBase
{
    public event GetEmailer GetEmailer;

    private IEmailer _emailer;

    protected IEmailer Emailer
    {
        get
        {
            if (_emailer == null)
            {
                GetEmailer(ref _emailer);
            }
            return _emailer;
        }
    }

    protected Dictionary<string,string> Arguments { get; set; }

    public void Execute()
    {
        // Perform some common logic here
        ExecuteAction(); // Execute the custom action
    }

    public abstract ResultBase ExecuteAction();
}

This is one of my custom actions:

public class SimpleEmailSender : ActionBase
{
    public override ResultBase ExecuteAction()
    {
        Emailer.Send(Body);
    }
}

Keep in mind that EventTriggerActionService resides in an isolated library, and will be used by different consumer applications. The consumer applications can choose to use IoC containers or just do Poor Man's DI.

My question is, is there a more optimal solution to my problem? I think my solution definitely addresses the issue of forcing the dependencies to all derived actions.





Aucun commentaire:

Enregistrer un commentaire