【问题标题】:StructureMap - Circular Dependencies and Lazy Initialization of Setter PropertiesStructureMap - Setter 属性的循环依赖和延迟初始化
【发布时间】:2013-08-10 14:44:29
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

    例如:

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

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

    如何做到这一点的方法是:1)StructureMap(IoC 顶部)和 2)Castle(代理顶部)库。下面我将展示此解决方案所需的一些对象的 sn-ps。更多内容可以在开源项目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 : 实例 { 受保护的只读 ProxyGenerator Factory = new ProxyGenerator(); protected readonly Type 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. 由于生命周期是基于线程或请求的,我们在运行时/网络请求中只有一个实现者
    2. 因为我们确实在使用 setter 注入时注入了 Wrappers,所以循环无限循环没有问题。每次只注入第一级

    【讨论】:

      猜你喜欢
      • 2020-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-08
      • 2010-12-22
      • 1970-01-01
      • 2021-10-09
      • 1970-01-01
      相关资源
      最近更新 更多