因此,我们假设我们遇到了一个看起来像StructureMap和IoC容器这个非常常见的问题。双向/循环依赖。
鉴于以下代码,它目前正在导致循环依赖,因为我们拥有“自动装配”属性。
public class ServiceA:IServiceA
{
public IServiceB ServiceBDependency {get;set;}
}
public class ServiceB:IServiceB
{
public IServiceA ServiceADependency {get;set;}
}
我看到每一个的'依赖'在彼此之间,但是,我觉得作为一个属性,它们不是真正的依赖,这是它们与使用构造函数注入的区别。
似乎应该有一种解决这些服务的方法......然后在创建对象后注入属性?
我知道解决这个问题的各种方法,包括重新架构我的服务的真正干净方式,但我很好奇我在使用StructureMap进行实例化和服务注册时有哪些选择。这似乎是一个需要解决方案的相当普遍的问题。
答案 0 :(得分:0)
我想告诉你我的方法。我只使用二传手注射。在我们的网络应用程序中,我们经常有许多对象相互引用。例如。 IUserFacade 在用户创建时需要 IUserState ,而 IUserState 在 userState上需要 IUserFacade 删除(检查约束)。
e.g:
public interface IUserFacade
{
... // User facade Add, Update, Delete
IUserStateFacade { set; }
}
public interface IUserStateFacade
{
...
IUserFacade { set; }
}
实际上,我们有许多交叉引用的对象,甚至更复杂。如果所有引用的对象都应该每次创建,即使在请求期间没有使用,也会非常有用。我们需要“懒惰”,将代理对象放在setter中。
如何做到这一点的方法是:1)StructureMap(IoC top)和2)Castle(代理顶部)库。下面我将展示此解决方案所需的一些对象片段。在开源项目Catharsis
中可以找到更多内容包装。此对象将由SM(StructureMap)而不是实际实现者注入每个Property。它是睡眠实现。它正在等待第一次通话。如果它永远不会发生(IUserFacade正在删除用户,在此类请求期间无需访问引用的IUserStateFacade)此包装器将永远休眠(请求)。一旦触摸,SM将创建真实对象,Wrapper将通过所有调用。
城堡拦截器:
public class Wrapper : IInterceptor
{
object _lazy;
protected readonly Type Type;
public Wrapper(Type type)
{
Type = type;
}
public void Intercept(IInvocation invocation)
{
if (_lazy.IsNull()) // lazily instantiate the instance
{
_lazy = ObjectFactory.GetInstance(Type);
}
try
{
var method = invocation.Method;
if (method.ContainsGenericParameters)
{
method = method.MakeGenericMethod(invocation.GenericArguments);
}
invocation.ReturnValue = method.Invoke(_lazy, invocation.Arguments);
}
catch (TargetInvocationException ex)
{
// PublishingManager.Publish(.... // publish exception
throw;
}
}
}
ProxyInstance。现在,我们需要一个对SM清晰可理解的对象。该对象将映射到所有接口(IUserFacade ...)而不是返回UserFacade
的实现。
我们也可以在这里使用我们的自定义AOP过滤器。
此ProxyInstance将提供真实的实现者类型,并构建Wrapper。
StructureMap实例:
公共类ProxyInstance:实例 { protected readonly ProxyGenerator Factory = new ProxyGenerator(); protected readonly类型ConcreteType;
public ProxyInstance(Type type)
{
ConcreteType = type; // the type for our Wrapper, the real implementation
}
protected override object build(Type pluginType, BuildSession session)
{
var aopFilters =
// my custom way how to inject more AOP filters
AopFilterManager.GetFilters()
// the core for us, one of the interceptors is our Wrapper
.Union(new[] { new Wrapper(ConcreteType) })
.ToArray();
// Castle will emit a proxy for us, but the Wrapper will do the job
var proxy = Factory
.CreateClassProxy(ConcreteType, AopFilterManager.AopOptions, aopFilters);
return proxy;
}
现在只需使用一些标准方法如何在SM 中映射它(我使用的是自定义ProxyConvention,但它不在此范围内)。让我们使用简化的显式映射:
registry
.For<IUserFacade>()
.HybridHttpOrThreadLocalScoped()
.Use(new ProxyInstance(typeof(UserFacade)));
...
此外,我们通过SM创建的每个对象都实现了IService。因此,默认的setter注入可以设置如下:
registry.SetAllProperties
(
set => set.TypeMatches
(
type => type
.GetInterfaces()
.Any(i => i.IsEquivalentTo(typeof(IService)))
)
);
从那一刻开始,当我们需要使用IUserFacade
(直接的ObjectFactory调用,或通过Wrapper访问)时,我们接收了真正的实现者。它的所有属性(setter注入)都将使用我们的ProxyInstance / Wrapper进行预填充。
如果访问任何这些属性,例如IUserStateFacade
同样(上面针对IUserFacade描述)将再次发生。