【问题标题】:How to use Dependency Injection (unity) when dynamically invoke method in a dll?在dll中动态调用方法时如何使用依赖注入(统一)?
【发布时间】:2018-07-30 23:07:43
【问题描述】:

我有一个从 dll 动态调用方法的项目。 dll 名称在配置文件中定义。下面是调用“Run”方法的代码。

Assembly a = Assembly.LoadFile(fullpath);
Type t = a.GetType(dll_fullname);
// Run in subclass
MethodInfo mi = t.GetMethod("Run");
if (mi != null)
{
    // set log file name
    object result = null;
    object classInstance = Activator.CreateInstance(t, null);
    object[] parametersArray = new object[] { t.Name };
    result = mi.Invoke(classInstance, parametersArray);
}
else
{
    myEventLog.WriteEntry("Error: Invoke DLL Failed.", EventLogEntryType.Error, (int)(SharedClass.EventId.Error));
}

每个 DLL 都是一个类,它继承自同一个基类 MyTask,并重写方法 Run。现在,我们想在每个类中使用 Unity 的依赖注入。显然,我们可以在每个 DLL 中应用 Unity。但是,我有一个问题:

由于所有 DLL 都继承自同一个基类 MyTask,是否可以在调用方法“Run”时进行依赖注入?我认为我们可以在 CreateInstance 时做到这一点,传递参数进行注入。但是,不同的 DLL 可能需要注入不同的服务。所以,我被困在这里了。

以前有人遇到过这种情况吗?有什么建议吗?

谢谢

【问题讨论】:

  • 你不能使用 DI。您必须重构代码才能使用 DI。你真的使用手动 DI。
  • @M.Hassan 当然可以,看看下面的答案

标签: c# .net dependency-injection


【解决方案1】:

由于您是在运行时加载类型,因此您需要一个工厂。好吧,那家工厂可以接受相同的注射,然后将它们传递下去。例如:

public class Factory : IFactory
{
    protected readonly IDependency1 _dependency1; //Injected
    protected readonly IDependency2 _dependency2; //Injected

    public Factory(IDependency1 dependency1, IDependency2 dependency2)
    {
        _dependency1 = dependency1;
        _dependency2 = dependency2;
    }

    public BaseClass Resolve(string libraryName, string typeName)
    {
        var assembly = Assembly.LoadFile(libraryName);
        var type = assembly.GetType(typeName);
        var args = new object [] { _dependency1, _dependency2 };
        return (BaseClass)Activator.CreateInstance(type, args);
    }
}

然后你注册工厂:

public static UnityContainer CompositionRoot()
{
    var container = new UnityContainer();
    container.RegisterType<IDependency1, Dependency1>();
    container.RegisterType<IDependency2, Dependency2>();
    container.RegisterType<IFactory,Factory>();
    return container;
}

并将其注入需要外部类的类中:

public class Application
{
    protected readonly IFactory _factory;

    public Application(IFactory factory)
    {
        _factory = factory;
    }

    public void Run()
    {
        var instance = _factory.Resolve("MyLibrary.dll", "External.DerivedClass");
        //Do something with instance
    }
}

并且依赖关系可以很好地传递。

如果您发现不同的库需要不同的注入,您可以在工厂中管理所有这些,这就是那种逻辑所属的地方。

如果派生类型有不同的构造函数参数和不同的注入,或者参数的顺序未知,你可以像这样用一点 LINQ 解决依赖关系:

protected object TryInject(Type concreteType, IEnumerable<object> dependencies)
{
    var constructors = concreteType
        .GetConstructors
        (
            BindingFlags.Public | BindingFlags.Instance
        )
        .OrderByDescending
        (
            c => c.GetParameters().Length
        );
    foreach (var c in constructors)
    {
        var parameters = c.GetParameters();
        var arguments = parameters
            .Select
            (
                p => dependencies.FirstOrDefault
                (
                    d => p.ParameterType.IsAssignableFrom(d.GetType())
                )
            )
            .ToArray();
        if (!arguments.Contains( null ))
        {
            return Activator.CreateInstance(concreteType, arguments);
        }
    }
    return null;
}

然后只需将你的依赖项传递给一个数组,它就会找出需要哪些以及将它们放在哪里:

return (BaseClass)TryInject(type, args);

见我的full working example on DotNetFiddle

【讨论】:

  • Func 注入器会以一种优雅的方式来代替 Factory,例如 Func&lt;string,string,Base&gt;,其中字符串参数是运行时提供的库和类型名称,ande 可用于任何运行时调用的类型数
  • @MrinalKamboj 您介意发布答案并提供示例实现吗?我一直想知道如何在不使用更多服务定位器模式的情况下做到这一点。
  • 使用 Ninject DI 框架在下面发布的答案,我没有检查 Unity API,但我认为它有类似的选项
  • 感谢您的努力@MrinalKamboj。我认为当您切换到使用工厂方法而不是类时,没有地方可以注入需要传递的依赖项,因此您最终使用了服务定位器(反?)模式。这可能没问题,这取决于 OP 计划如何在他的项目和他的单元测试套件中使用它,但是如果 OP 正在构建一个可插入的架构,那么编写工厂类可能是值得的,如果没有其他原因只是为了服务作为所需特殊逻辑的容器。
  • 感谢@John,在我的理解中,直到点代码为止,构造函数参数作为显式依赖传递,Activator.CreateInstance(type, args),它仍然是服务定位器反模式,否则 DI 需要在没有显式构造函数参数的情况下无缝插入.在我的示例中,我也可以将 Func 注入器更改为 Func&lt;string,string,Dependency1,Base1&gt;,以从 Test 类中获取 Dependency1 并在构造函数中显式注入。对于通过反射 (Activator.CreateInstance) 实例化的类,我们的示例都没有服务定位器反模式
猜你喜欢
  • 1970-01-01
  • 2011-09-25
  • 2018-09-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-11-28
  • 2014-09-12
  • 1970-01-01
相关资源
最近更新 更多