我有一个项目,可以从dll动态调用方法。 dll名称在配置文件中定义。下面是调用“运行”方法的代码。
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继承的,因此在调用方法“运行”时是否可以进行依赖项注入?我认为我们可以在CreateInstance传递注入参数时做到这一点。但是,不同的DLL可能需要注入不同的服务。所以,我被困在这里。
以前有人遇到过类似情况吗?有什么建议吗?
谢谢
答案 0 :(得分:2)
由于要在运行时加载类型,因此需要工厂。好吧,那个工厂可以接受相同的注射,然后将它们传递下去。例如:
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);
答案 1 :(得分:0)
签出以下代码,该代码使用Ninject DI框架完成,它使用Func
注入在运行时执行逻辑:
void Main()
{
var kernel = new StandardKernel();
kernel.Load(Assembly.GetExecutingAssembly()); // Load from Bindings (derived from NinjectModule)
var test = kernel.Get<Test>();
test.DoWork("libraryName","typeName");
}
public class Base
{
public virtual void DoWork()
{
Console.WriteLine("Base");
}
}
public class Derived : Base
{
public override void DoWork()
{
Console.WriteLine("Derived");
}
}
public class Test
{
private Func<string,string,Base> BaseFunc {get;}
public Test(Func<string,string,Base> baseFunc)
{
BaseFunc = baseFunc;
}
public void DoWork(string libraryName, string typeName)
{
BaseFunc(libraryName,typeName).DoWork();
}
}
public class Bindings : NinjectModule
{
public override void Load()
{
Bind<Func<string, string, Base>>().ToMethod(ctx => (libraryName,typeName) =>
{
Assembly a = Assembly.LoadFile(libraryName);
Type t = a.GetType(typeName);
object classInstance = Activator.CreateInstance(t, null);
return (Base)classInstance;
});
}
}
说明:
Base
类是基本类型,在运行时需要使用relfection来调用Derived
类型Func<string,string,Base> BaseFunc
,它在运行时获取fullpath
和dllfullpath
之类的参数Main
方法是测试方法,它在运行时获取Test对象,并调用DoWork
方法在运行时注入相关对象如果您只想使用Unity,那么它只需要一个类似的ToMethod
选项即可为Func注入提供帮助。
编辑1:
根据@JohnWu的建议,假设Derived
类还需要注入派生类Base1
的基类Derived1
,现在这是可能的,但是我们需要显式的构造函数参数ctx.Kernel.Get<Base1>()
,如下面的代码所示。这不是真正的DI,而是服务位置,但这是使用反射创建Base类的实例的限制,自动DI可以在使用DI框架创建聚合类的情况下工作。
public class Base1
{
public virtual void DoWork()
{
Console.WriteLine("Base..1");
}
}
public class Derived1 : Base1
{
public override void DoWork()
{
Console.WriteLine("Derived..1");
}
}
派生类将修改为:
public class Derived : Base
{
private Base1 MyBase1 { get;}
public Derived(Base1 myBase1)
{
MyBase1 = myBase1;
}
public override void DoWork()
{
Console.WriteLine("Derived..0");
MyBase1?.DoWork();
}
}
使用反射创建对象实例将是:
var classInstance = Activator.CreateInstance(t, ctx.Kernel.Get<Base1>());
return (Base)classInstance;
这将调用构造函数:
public Derived(Base1 myBase1)