StructureMap注入集合时重复IBar实例问题及解决方案问询
我完全懂你的困扰——不能动全局的IBar配置(毕竟其他业务模块还依赖它当前的生命周期),但又得让每个IFoo实例都拿到独一份的Bar对象。下面给你几个实用的解决方案,按需选就行:
方案1:给每个IFoo实现单独配置依赖规则
这是最直接的做法,不用碰全局IBar的配置,只需要针对每个IFoo的实现类,明确告诉StructureMap:给这个类注入IBar的时候,每次都新建一个实例(也就是Transient生命周期)。
var container = new Container(_ => { _.Scan(x => { x.TheCallingAssembly(); x.AddAllTypesOf<IFoo>(); }); // 全局IBar配置原封不动保留 _.For<IBar>().Use<Bar>(); // 为每个IFoo实现单独指定IBar的注入规则 _.For<Foo1>().Ctor<IBar>().Is<Bar>().Transient(); _.For<Foo2>().Ctor<IBar>().Is<Bar>().Transient(); });
这样一来,创建Foo1或Foo2时,StructureMap会专门为它们生成新的Bar实例,而其他地方请求IBar时,还是遵循原来的全局配置,不会影响现有业务。
方案2:用条件绑定批量处理所有IFoo实现
如果你的IFoo实现类很多,一个个配置太麻烦,那就用StructureMap的条件绑定功能——只要IBar是被IFoo的子类依赖的,就自动用Transient生命周期,其他场景依然沿用全局配置:
var container = new Container(_ => { _.Scan(x => { x.TheCallingAssembly(); x.AddAllTypesOf<IFoo>(); }); // 全局IBar配置不变 _.For<IBar>().Use<Bar>(); // 核心规则:当IBar被IFoo的实现类依赖时,每次都新建实例 _.For<IBar>() .When(context => context.ParentType?.IsAssignableTo(typeof(IFoo)) == true) .Use<Bar>() .Transient(); });
这个方案的好处是扩展性极强——以后新增IFoo的实现类,完全不用额外配置,自动就能拿到独立的Bar实例。
方案3:用工厂模式手动生成IBar实例
如果上面的容器配置方式不符合你的需求,也可以换个思路,在Foo类里通过工厂来创建Bar,彻底绕开容器的生命周期限制。不过这个方法需要修改Foo的代码:
首先定义一个简单的工厂接口和实现:
public interface IBarFactory { IBar Create(); } public class BarFactory : IBarFactory { public IBar Create() => new Bar(); }
然后修改Foo的构造函数,依赖工厂而不是直接依赖IBar:
public class Foo1 : IFoo { public IBar bar { get; set; } public Foo1(IBarFactory barFactory) { this.bar = barFactory.Create(); } } public class Foo2 : IFoo { public IBar bar { get; set; } public Foo2(IBarFactory barFactory) { this.bar = barFactory.Create(); } }
最后在容器里注册工厂:
var container = new Container(_ => { _.Scan(x => { x.TheCallingAssembly(); x.AddAllTypesOf<IFoo>(); }); _.For<IBar>().Use<Bar>(); // 全局配置保留 _.For<IBarFactory>().Use<BarFactory>(); // 注册工厂 });
这样每个Foo实例都会通过工厂创建全新的Bar,完全不受全局IBar生命周期的影响,同时其他模块的IBar使用也不会被干扰。
验证效果
不管用哪种方案,运行你的测试代码:
var foos = container.GetAllInstances<IFoo>(); if (foos.ElementAt(0).bar.id == foos.ElementAt(1).bar.id) throw new Exception("Bar must be a different instance");
现在都不会再抛出异常了,每个Foo的bar都是独立的Bar实例。
内容的提问来源于stack exchange,提问作者Daryl Van Sittert

