Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ionic-framework/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# &引用;“随需应变”;动态实例化类的依赖注入_C#_Events_Reflection_Dependency Injection_Delegates - Fatal编程技术网

C# &引用;“随需应变”;动态实例化类的依赖注入

C# &引用;“随需应变”;动态实例化类的依赖注入,c#,events,reflection,dependency-injection,delegates,C#,Events,Reflection,Dependency Injection,Delegates,在我的应用程序中,我有一个名为EventTriggerActionService的类,它负责实例化驻留在不同程序集中的自定义操作。它使用类名和程序集名通过反射加载模块,并执行它 我的主要问题是:如何将所需的依赖项注入到这些自定义操作中?我的操作都来自一个名为ActionBase的基类 我最初的解决方案是首先通过构造函数注入提供所有依赖项,只需将它们作为参数添加到(ActionBase)Activator.CreateInstance()中,但这是有问题的,因为我不想强制所有派生操作都采用其他操作

在我的应用程序中,我有一个名为
EventTriggerActionService
的类,它负责实例化驻留在不同程序集中的自定义操作。它使用类名和程序集名通过反射加载模块,并执行它

我的主要问题是:如何将所需的依赖项注入到这些自定义操作中?我的操作都来自一个名为
ActionBase
的基类

我最初的解决方案是首先通过构造函数注入提供所有依赖项,只需将它们作为参数添加到
(ActionBase)Activator.CreateInstance()中,但这是有问题的,因为我不想强制所有派生操作都采用其他操作的依赖项

这是我的第二个解决方案:我决定使用事件将“依赖提供程序”放入
EventTriggerActionService
中。使用这种方法,所有自定义操作的所有依赖项都将被注入
EventTriggerActionService
,并且自定义操作将通过触发事件来请求依赖项

这是我的
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
    }

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

public delegate void GetEmailer(ref IEmailer emailer);
这是
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();
}
请记住,
EventTriggerActionService
驻留在一个独立的库中,将由不同的使用者应用程序使用。消费者应用程序可以选择使用IoC容器,也可以只执行穷人的DI


我的问题是,我的问题有没有更理想的解决方案?我认为我的解决方案明确地解决了将依赖关系强制到所有派生操作的问题。

我认为您的主要问题在于基类
ActionBase

这个类中的
Action
方法基本上是一个包装器,它在调用实际操作之前执行一些公共逻辑。 众所周知,需要一些外部服务(这里是
IEmailer
)才能发挥作用,您不希望将这些依赖关系强加于派生类


使用事件的更简单方法可能是公开
IEmailer
属性,并在
EventTriggerActionService.Execute
方法中创建操作时进行设置

这将防止您必须通过构造函数传递引用,同时仍然能够访问
IEmailer实例
。 你会得到一些大致如下的东西:

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
}
公共抽象类ActionBase
{
受保护的IEmailer电子邮件程序{get;set;}
受保护的字典参数{get;set;}
public void Execute()
{
//用Emailer做点什么。
ExecuteAction();//执行自定义操作
}
公共摘要结果数据库ExecuteAction();
}
公共类EventTriggerActionService
{
//为便于阅读而省略
public void Execute(EventTriggerAction EventTriggerAction、EventContext上下文)
{   
//为便于阅读而省略
var action=(ActionBase)Activator.CreateInstance();//通过其基类ActionBase实例化操作
action.Arguments=data.Arguments;//这是一个包含操作参数的字典
action.Emailer=_emailerFactory.Create();
//为便于阅读而省略
}
//为便于阅读而省略
}

但是,既然您可以控制调用操作的方式,为什么要将公共逻辑强制到基类中呢

您可以轻松提取通用逻辑并将其与操作分离。
例如:

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
}
公共接口IActionBase
{
ResultBase ExecuteAction();
}
公共类SimpleEmailSender:IActionBase
{
私有字典参数;
私人电子邮件;
公共SimpleEmailSender(字典参数、IEmailer emailer)
{
this.arguments=参数;
this.emailer=emailer;
}
公共覆盖ResultBase ExecuteAction()
{
this.emailer.Send(参数[“EmailBody”]);
返回null;
}
}   
公共类EventTriggerActionService
{
//为便于阅读而省略
public void Execute(EventTriggerAction EventTriggerAction、EventContext上下文)
{   
//为便于阅读而省略
这是RunPreLogic();
action.ExecuteAction();
这是RunPostLogic();
//为便于阅读而省略
}
public void RunPreLogic(){}
public void RunPostLogic(){}
//为便于阅读而省略
}
正如您从示例中看到的,该逻辑已从基类中删除。从提供这些服务的需要中释放任何派生类。 剩下的唯一一件事就是将所需的服务注入到所创建类的构造函数中

我过去使用的DI容器总是提供某种注入服务的方式。如果您不能(或不想)使用容器,那么提取构造函数信息并自己解决它应该不会太难

如果您使用的是ASP.NET核心,默认的Microsoft依赖项注入将为此()提供实用程序类
ActivatorUtilities



如果您不希望在
EventTriggerActionService
中使用公共逻辑,您可以将其提取到单独的服务中,或者执行处理公共逻辑的特殊操作

因为
EventTriggerActionService
不知道它处理的是什么类型,它必须让自定义操作决定它们想要什么。所以,我认为服务定位器模式是一个很好的解决方案。假设您在
EventTriggerActionService
侧定义了服务定位器,如:

public static class ServiceLocator
{
    private static Dictionary<string, Type> container = new Dictionary<string, Type>();

    public static void RegisterService(string name, Type implType)
    {
        container.Add(name, implType);
    }

    public static objcet GetService(string name)
    {
        return Actinator.CreateInstance(container[name]);
    }
}
最后,在
EventTriggerActionService
中,在创建自定义操作的实例之前注册所有依赖项

public class EventTriggerActionService
{
    static EventTriggerActionService()
    {
        ServiceLocator.RegisterService("name1", typeof(dependencyImpl1));
        ...
        ServiceLocator.RegisterService("nameN", typeof(dependencyImplN));
    }

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

        Assembly actionHandlerAssembly = Assembly.Load(assemblyName);  
        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.Execute(); // Execute the Action
    }
}

那里的所有代码都只是为了显示想法。您可以自定义
ServiceLocator
或其他适合您的功能。

@DavidG请记住,我提供的代码片段只是一个原型。我要20个+
public abstract class ActionBase
{
    private static MethodInfo getServiceMI;

    static ActionBase()
    {
        // Find out which assembly implement ServiceLocator.
        var asm = AppDomain.CurrentDomain.GetAssemblies().First(x => x.GetTypes().Any(y => y.Name == "ServiceLocator"));
        // Get MethodInfo of ServiceLocator.GetService.
        getServiceMI = asm.GetType("ServiceLocator").GetMethod("GetService", BindingFlags.Public | BindingFlags.Static);
    }

    protected static object GetService(string name)
    {
        return getServiceMI.Invoke(null, new object[] { name });
    }

    // And your original staff.
}

public class ActionSample : ActionBase
{
    IDependency1 d1 = GetService("name1");
    ...
    IDependencyN dN = GetService("nameN");

    public override ResultBase ExecuteAction()
    {
        d1.DoSomething();
        ...
        dN.DoSomething();
    }
}
public class EventTriggerActionService
{
    static EventTriggerActionService()
    {
        ServiceLocator.RegisterService("name1", typeof(dependencyImpl1));
        ...
        ServiceLocator.RegisterService("nameN", typeof(dependencyImplN));
    }

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

        Assembly actionHandlerAssembly = Assembly.Load(assemblyName);  
        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.Execute(); // Execute the Action
    }
}