StructureMap - Setter属性的循环依赖和延迟初始化

时间:2013-08-10 14:44:30

标签: c# .net inversion-of-control ioc-container structuremap

因此,我们假设我们遇到了一个看起来像StructureMap和IoC容器这个非常常见的问题。双向/循环依赖。

鉴于以下代码,它目前正在导致循环依赖,因为我们拥有“自动装配”属性。

public class ServiceA:IServiceA
{
     public IServiceB ServiceBDependency {get;set;}
}

public class ServiceB:IServiceB
{
     public IServiceA ServiceADependency {get;set;}
}

我看到每一个的'依赖'在彼此之间,但是,我觉得作为一个属性,它们不是真正的依赖,这是它们与使用构造函数注入的区别。

似乎应该有一种解决这些服务的方法......然后在创建对象后注入属性?

我知道解决这个问题的各种方法,包括重新架构我的服务的真正干净方式,但我很好奇我在使用StructureMap进行实例化和服务注册时有哪些选择。这似乎是一个需要解决方案的相当普遍的问题。

1 个答案:

答案 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描述)将再次发生。

  1. 因为生命周期是基于线程或请求的,所以我们在运行时/ Web请求中只有一个实现者
  2. 因为我们在使用setter注入时注入了Wrappers,所以圆形无限循环没有问题。
  3. 每次只注入第一级