Ninject:如何将泛型类型动态注入到绑定模板对象中
Ninject: How To Dynamically Inject Generic Type into Bound Template Object
我在 Ninject 配置中配置了一个通用模板 class。
模板Class:
public Repository<T> : IRepository<T>
{
//...
}
Ninject 配置:
container.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
此外,我有一个class需要动态创建IRepository<T>
的多个实例,其中T对于每个实例都是动态的。
有没有办法用 Ninject 做到这一点?伪代码如下:
foreach(Type genericType in repositoryTypes)
{
var tempRepository = WebManager.Get(IRepository<genericType>); //how should this actually be?
//do stuff with tempRepository
}
WebManager 会像下面这样:
public static class WebManager
{
public static object Get(Type t)
{
object service = GlobalConfiguration.Configuration.DependencyResolver.GetService(t);
return service;
}
}
当然可以,很简单:
foreach(Type genericType in repositoryTypes)
{
object tempRepository = WebManager.Get(typeof(IRepository).MakeGenericType(genericType));
//do stuff with tempRepository
}
但接下来会变得更加棘手。你将如何使用这个 object
?你需要使用反射。使用一次比每次调用更简单。所以我们可以adjust/write代码如下:
internal class Foo
{
private static readonly MethodInfo DoStuffToRepositoryForMethod =
typeof(Foo).GetMethod(
"DoStuffToRepositoryFor",
BindingFlags.Instance | BindingFlags.NonPublic);
private readonly IResolutionRoot resolutionRoot;
public Foo(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public void DoStuffToRepositories(params Type[] entityTypes)
{
foreach (Type entityType in entityTypes)
{
MethodInfo doStuffMethod = DoStuffToRepositoryForMethod
.MakeGenericMethod(entityType);
doStuffMethod.Invoke(this, new object[0]);
}
}
private void DoStuffToRepositoryFor<T>()
{
var repository = this.resolutionRoot.Get<IRepository<T>>();
repository.DoSomething();
}
}
仅供参考,这是一个表明它有效的测试:
public class Test
{
[Fact]
public void TestIt()
{
var kernel = new StandardKernel();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
var foo = kernel.Get<Foo>();
foo.DoStuffToRepositories(typeof(string), typeof(int));
}
}
如无必要,请不要使用魔法
但我认为更重要的问题是:你为什么要这样做?
您如何知道实体类型列表和要为其做某事的实体?
在大多数情况下,有一种替代方法不涉及那么多 "magic".
我在 Ninject 配置中配置了一个通用模板 class。
模板Class:
public Repository<T> : IRepository<T>
{
//...
}
Ninject 配置:
container.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
此外,我有一个class需要动态创建IRepository<T>
的多个实例,其中T对于每个实例都是动态的。
有没有办法用 Ninject 做到这一点?伪代码如下:
foreach(Type genericType in repositoryTypes)
{
var tempRepository = WebManager.Get(IRepository<genericType>); //how should this actually be?
//do stuff with tempRepository
}
WebManager 会像下面这样:
public static class WebManager
{
public static object Get(Type t)
{
object service = GlobalConfiguration.Configuration.DependencyResolver.GetService(t);
return service;
}
}
当然可以,很简单:
foreach(Type genericType in repositoryTypes)
{
object tempRepository = WebManager.Get(typeof(IRepository).MakeGenericType(genericType));
//do stuff with tempRepository
}
但接下来会变得更加棘手。你将如何使用这个 object
?你需要使用反射。使用一次比每次调用更简单。所以我们可以adjust/write代码如下:
internal class Foo
{
private static readonly MethodInfo DoStuffToRepositoryForMethod =
typeof(Foo).GetMethod(
"DoStuffToRepositoryFor",
BindingFlags.Instance | BindingFlags.NonPublic);
private readonly IResolutionRoot resolutionRoot;
public Foo(IResolutionRoot resolutionRoot)
{
this.resolutionRoot = resolutionRoot;
}
public void DoStuffToRepositories(params Type[] entityTypes)
{
foreach (Type entityType in entityTypes)
{
MethodInfo doStuffMethod = DoStuffToRepositoryForMethod
.MakeGenericMethod(entityType);
doStuffMethod.Invoke(this, new object[0]);
}
}
private void DoStuffToRepositoryFor<T>()
{
var repository = this.resolutionRoot.Get<IRepository<T>>();
repository.DoSomething();
}
}
仅供参考,这是一个表明它有效的测试:
public class Test
{
[Fact]
public void TestIt()
{
var kernel = new StandardKernel();
kernel.Bind(typeof(IRepository<>)).To(typeof(Repository<>));
var foo = kernel.Get<Foo>();
foo.DoStuffToRepositories(typeof(string), typeof(int));
}
}
如无必要,请不要使用魔法
但我认为更重要的问题是:你为什么要这样做? 您如何知道实体类型列表和要为其做某事的实体? 在大多数情况下,有一种替代方法不涉及那么多 "magic".