我正在使用Unity IoC容器,我需要拦截对Resolve的任何调用以获取某个基本接口,并运行我自己的自定义代码来构造这些类型。
换句话说,在下面的示例代码中,当我调用container.Resolve<IFooN>()
时,如果它没有具体实现类型的实例,则调用MyFactoryFunction
来构造一个,否则我想要它返回缓存的副本。
标准Unity容器无法构造这些对象(更新:,因为它们是.NET远程处理对象,因此本地计算机上的任何程序集中都不存在具体类),而我不想预先创建它们并使用RegisterInstance存储它们。
interface IFoo : IBase { ... }
interface IFoo2 : IBase { ... }
...
container.Resolve<IFoo2>();
...
IBase MyFactoryFunction(Type t)
{
...
}
我假设我可以创建一个Unity扩展来执行此操作,但我想知道是否已有解决方案我可以借用。
答案 0 :(得分:8)
为了完整起见,我应该添加另一个在Unity 2下运行的答案,因为我的其他答案不再适用。由于您需要制定自定义构建器策略,因此需要稍微多一些。感谢Unity项目中的ctavares,他们在this thread中提供了很多帮助来实现这一点:
public class FactoryUnityExtension : UnityContainerExtension
{
private ICustomFactory factory;
private CustomFactoryBuildStrategy strategy;
public FactoryUnityExtension(ICustomFactory factory)
{
this.factory = factory;
}
protected override void Initialize()
{
this.strategy = new CustomFactoryBuildStrategy(factory, Context);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
Context.Policies.Set<ParentMarkerPolicy>(new ParentMarkerPolicy(Context.Lifetime), new NamedTypeBuildKey<ParentMarkerPolicy>());
}
}
public class ParentMarkerPolicy : IBuilderPolicy
{
private ILifetimeContainer lifetime;
public ParentMarkerPolicy(ILifetimeContainer lifetime)
{
this.lifetime = lifetime;
}
public void AddToLifetime(object o)
{
lifetime.Add(o);
}
}
public interface ICustomFactory
{
object Create(Type t);
bool CanCreate(Type t);
}
public class CustomFactoryBuildStrategy : BuilderStrategy
{
private ExtensionContext baseContext;
private ICustomFactory factory;
public CustomFactoryBuildStrategy(ICustomFactory factory, ExtensionContext baseContext)
{
this.factory = factory;
this.baseContext = baseContext;
}
public override void PreBuildUp(IBuilderContext context)
{
var key = (NamedTypeBuildKey)context.OriginalBuildKey;
if (factory.CanCreate(key.Type) && context.Existing == null)
{
context.Existing = factory.Create(key.Type);
var ltm = new ContainerControlledLifetimeManager();
ltm.SetValue(context.Existing);
// Find the container to add this to
IPolicyList parentPolicies;
var parentMarker = context.Policies.Get<ParentMarkerPolicy>(new NamedTypeBuildKey<ParentMarkerPolicy>(), out parentPolicies);
// TODO: add error check - if policy is missing, extension is misconfigured
// Add lifetime manager to container
parentPolicies.Set<ILifetimePolicy>(ltm, new NamedTypeBuildKey(key.Type));
// And add to LifetimeContainer so it gets disposed
parentMarker.AddToLifetime(ltm);
// Short circuit the rest of the chain, object's already created
context.BuildComplete = true;
}
}
}
答案 1 :(得分:5)
更新这个答案适用于Unity 1.2。有关适用于Unity 2的解决方案,请参阅我的其他答案。
好的,我自己实施了扩展程序。在构建器中,我将对象缓存,因为我希望它是一个单独的w.r.t我的容器。 baseContext
的原因是我希望它被缓存在顶级容器中,而不是缓存在请求它的任何子容器中。
public class FactoryMethodUnityExtension<T> : UnityContainerExtension
{
private Func<Type,T> factory;
public FactoryMethodUnityExtension(Func<Type,T> factory)
{
this.factory = factory;
}
protected override void Initialize()
{
var strategy = new CustomFactoryBuildStrategy<T>(factory, this.Context);
Context.Strategies.Add(strategy, UnityBuildStage.PreCreation);
}
}
public class CustomFactoryBuildStrategy<T> : BuilderStrategy
{
private Func<Type,T> factory;
private ExtensionContext baseContext;
public CustomFactoryBuildStrategy(Func<Type,T> factory, ExtensionContext baseContext)
{
this.factory = factory;
this.baseContext = baseContext;
}
public override void PreBuildUp(IBuilderContext context)
{
var key = (NamedTypeBuildKey)context.OriginalBuildKey;
if (key.Type.IsInterface && typeof(T).IsAssignableFrom(key.Type))
{
object existing = baseContext.Locator.Get(key.Type);
if (existing == null)
{
// create it
context.Existing = factory(key.Type);
// cache it
baseContext.Locator.Add(key.Type, context.Existing);
}
else
{
context.Existing = existing;
}
}
}
}
添加扩展非常简单:
MyFactory factory = new MyFactory();
container = new UnityContainer();
container.AddExtension(new FactoryMethodUnityExtension<IBase>(factory.Create));
答案 2 :(得分:5)
Unity(v2)允许您指定工厂。它允许几个不同的功能,包括采用类型建立/名称等。简单的例子:
UnityContainer cont = new UnityContainer();
有一个简单的创建测试方法 - 但是可以使用您想要的任何工厂进行扩展 这将绕过正常的创作过程(为简洁起见)称之为 最长的构造函数 包括财产设定在内的所有后续行为仍将执行。
cont.RegisterType<TestClass>(new InjectionFactory(c => CreateTest()));
var svc = cont.Resolve<TestClass>();
答案 3 :(得分:-1)
Unity容器已经充当了一个知道如何构造(并执行依赖注入)任意类型的工厂,因此接受Type t的工厂看起来是多余的。你能详细说明为什么这是不可能的吗?
如果这真的不可能(更可能是太多的工作),那么也许你可以用容器注册你的工厂?