1

所以我们遇到了我认为的 StructureMap 和 IoC 容器的一个非常常见的问题。双向/循环依赖。

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

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

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

我看到了它们中的每一个相互之间的“依赖关系”,但是,我觉得作为一个属性,它们不是真正的依赖关系,这就是它们与使用构造函数注入的区别。

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

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

4

1 回答 1

0

我想向你展示我的方法。我只使用 setter 注入。在我们的网络应用程序中,我们经常有许多相互引用的对象。例如,IUserFacade在创建用户时需要IUserState ,而IUserState在删除userState时需要IUserFacade(检查约束)。

例如:

public interface IUserFacade
{
   ... // User facade Add, Update, Delete
   IUserStateFacade { set; }    
}
public interface IUserStateFacade
{
   ...
   IUserFacade { set; }
}

实际上,我们有很多具有交叉引用的对象,甚至更复杂。如果每次都创建所有引用的对象,即使在请求期间没有使用,这确实会非常昂贵。我们需要“懒惰”,将代理对象放置在设置器中。

如何做到这一点的方法是:1)StructureMap(IoC 顶部)和 2)Castle(代理顶部)库。下面我将展示此解决方案所需的一些对象片段。更多内容可以在开源项目Catharsis中找到

包装。该对象将由 SM(StructureMap)而不是真正的实现者注入到每个属性中。它是休眠的实现。它正在等待第一个呼叫。如果它永远不会发生(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;
        }
    }
}

代理实例。现在,我们需要一个对象,对 SM 来说清晰易懂。该对象将映射到所有接口(IUserFacade...),而不是返回UserFacade.

我们也可以在这里使用我们自定义的 AOP 过滤器。

这个 ProxyInstance 将提供真正的实现者类型,并构建 Wrapper。

StructureMap 实例:

公共类 ProxyInstance : Instance { protected readonly ProxyGenerator Factory = new ProxyGenerator(); 受保护的只读类型具体类型;

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. 因为生命周期是基于线程或请求的,所以我们在运行时/网络请求中只有一个实现者
  2. 因为我们在使用 setter 注入时确实注入了 Wrappers,所以循环无限循环没有问题。每次只注入第一级
于 2013-08-17T10:55:48.697 回答