在DryIOC 中,在使用一个或多个服务时是有用的数据结构。封装器使得使用主要包含两种类型:
- 将待封装的服务作为
开放式泛型 的类型参数。例如:Func<TService> , Lazy<TService> , IEnumerable<TService> - 非开放式泛型使用
必须的服务类型 。例如:LambdaExpression
开放式泛型封装福哦个类型参数仅能封装一个服务类型。例如,Func<TArg0, TArg1, TService> 仅封装了 TService 。 在一个封装器中封装多个服务是不支持的
更多信息:
- 封装器可以嵌套组合使用,例如:
IEnumerable<Lazy<TService>> - 与
Autofac 相似的概念是relatiionship type - 支持预定义与用户定义的封装器
- 显式的注册一个封装器类型作为一个服务重载对应的封装器注册
- 常规的开放式泛型服务与封装器实际的不同将在
用户定义封装器 小节中解释
一个样例:
void Main()
{
var container = new Container();
container.Register<A>();
container.Register<B>();
container.Resolve<B>();
}
class A { }
class B
{
public B(Lazy<A> a) { }
}
1. 预定义封装器
预定义封装器总是可以的,不需要注册它们,但可以解除注册。预定义的封装器类型被DryIOC 自动选择,来保持业务逻辑相对于某个特定IOC 容器的依赖。这意味着除DryIoc.Meta<,> 之外的所有封装器类型,在DryIOC 之外都是可用的。
DryIoc.Meta<,> 类型仅仅用于辅助从Autofac 进行迁移,但可以使用System.Typle<,> 进行替代。
1.1 Lazy of A
- 在第一次访问时创建一个
A 的实例 - 内部实现是调用
r => new Lazy(() => r.Resolve<A>()) ,因此在Lazy 注入时,A 可能是不可用的 - 允许递归依赖,因为在调用
Resolve 时,服务的创建被延迟
1.2 Func of A
- 通过委托创建
A 到用户代码 - 默认情况,服务通过内联服务进行创建:
new B(() => new A()) 。因此,A 需要在解析Func<A> 时需要处于可用状态 - 默认情况,不支持递归依赖
- 作为替代,内联
A 创建以可改变到Resolve 调用时:container.Register<A>(setup: Setup.With(asResolutionCall: true));
- 这种方式
Func<A> 被注入通过:new B(() => r.Resolver.Resolve<A>()) - 解析图中的递归依赖是被允许的
2. Lazy 与 Func的真实延迟
在解析Lazy 与Func 时,被封装的依赖应该被注册到容器中,否则会报错。延迟是指延迟解析,而不是延迟注册。
void Main()
{
var container = new Container();
container.Resolve<Lazy<A>>();
}
class A { }
延迟注册也是有方式进行的:
- 通过
RegisterPlaceHolder - 全局配置,
Rules.WithFuncAndLazyWithoutRegistration()
void Main()
{
var container = new Container(rules => rules.WithFuncAndLazyWithoutRegistration());
var getA = container.Resolve<Func<A>>();
container.Register<A>();
var a = getA();
}
class A { }
3. 带参数的Func
将服务的创建委托给用户,并允许提供依赖项的子集。其余依赖项通常由容器注入。更过信息:
- 它可以被看作是通过参数提供的对通常注入的依赖项与自定义依赖项的重写。
- 或者作为服务创建构造函数或方法的“柯里化”
- 有时Func 带有基元类型的类型参数,用于传递值
- 作为内联创建表达式注入
new B((dep1, dep2) => new A(dep1, new D(), dep2)) - 允许递归依赖
提供的参数按类型和传递顺序与参数匹配。如果未找到匹配的参数,则容器将注入该参数。参数顺序可能与参数顺序不同。如果提供的参数类型相同怎么办? DryIoc 将跟踪已使用的参数并在随后的匹配中跳过它们。意味着示例中的 dep1 和 dep2 可能是一个字符串并且仍然被使用。
如果未使用传递的参数,则 DryIoc 将继续匹配嵌套依赖项(如果有)cs new B(arg => new A(new D(arg)))
当未使用传递的参数时,它可能是一个错误,但也可能不是。这是一个艰难的选择,但 DryIoc 暂时会忽略这一点。
void Main()
{
var container = new Container();
container.Register<A>();
var getA = container.Resolve<Func<string, A>>();
getA("ignore me");
}
class A { }
还有一个与重用相关的警告。 您可能会说传递不同的参数来创建服务可能会产生不同的服务。 但是当服务注册为非瞬时重用时会发生什么? 默认情况下,DryIoc 将使用对 Func 的第一次调用来创建服务并忽略其余部分。
void Main()
{
var container = new Container();
container.Register<A>(Reuse.Singleton);
var getA = container.Resolve<Func<string, A>>();
Console.WriteLine("Hi, Alpha" == getA("Hi, Alpha").Greeting);
Console.WriteLine("Hi, Alpha" == getA("Hi, Beta").Greeting);
}
class A
{
public string Greeting { get; }
public A(string greeting) { Greeting = greeting; }
}
如果你不喜欢这种方式,你可以使用规则 Rules.WithIgnoringReuseForFuncWithArgs()
void Main()
{
var container = new Container(rules => rules.WithIgnoringReuseForFuncWithArgs());
container.Register<A>(Reuse.Singleton);
var getA = container.Resolve<Func<string, A>>();
Console.WriteLine("Hi, Alpha" == getA("Hi, Alpha").Greeting);
Console.WriteLine("Hi, Alpha" == getA("Hi, Beta").Greeting);
}
class A
{
public string Greeting { get; }
public A(string greeting) { Greeting = greeting; }
}
3.1 KeyValuePair of Service Key and A
将注册的服务密钥与解析的服务包装在一起。可用于根据其键过滤服务。通常与 Func 或 Lazy 包装器一起使用并嵌套在集合中:IEnumerable<KeyValuePair<string, Func<A>>>
3.2 Meta or Tuple of A with Metadata
将解析的服务 A 和关联的元数据对象打包在一起。有点类似于 KeyValuePair,但使用元数据而不是键。您可能会以与为实现类型定义的 Attribute 相同的方式考虑元数据。在MefAttributedModel 时,元数据以特性的方式提供。
void Main()
{
var container = new Container();
container.Register<A>(setup: Setup.With(metadataOrFuncOfMetadata: "XYZ"));
var a1 = container.Resolve<Meta<A, string>>();
var a2 = container.Resolve<Tuple<A, string>>();
a1.Dump("a1");
a2.Dump("a2");
}
class A{}
可以使用System.Tuple<,> 替代DryIoc.Meta<,> ,这样可以避免对DryIOC 的引用。 如果没有在setup 中提供元数据,而解析元数据,将抛出ContainerException 。元数据亦可以被用作集合过滤。当元数据嵌套在集合封装器中式,此时元数据没有注册或不可赋值,没有异常被抛出。
void Main()
{
var container = new Container();
var items = container.Resolve<Meta<A, int>[]>();
items.Dump();
}
class A { }
3.3 字典元数据
元数据值可以是非基元对象或 IDictionary<string, object>。在后一种情况下,您可以通过在字典中提供任何object 类型来进行解析。
void Main()
{
var container = new Container();
container.Register<A>(setup: Setup.With(new Dictionary<string, object>{{ "color", "red" },{ "quantity", 15 }}));
container.Register<A, B>(setup: Setup.With(new Dictionary<string, object>{{ "color", "red" },{ "special", true }}));
var items = container.Resolve<Meta<A, IDictionary<string, object>>[]>();
items.Dump();
var itemQuantities = container.Resolve<Meta<A, int>[]>();
itemQuantities.Dump();
}
class A { }
class B : A { }
3.4 IEnumerable or array of A
向使用者注入一组已注册的服务值。该集合基本上是一个完全初始化的“固定”数组:
var items = new B(new A[] { new Impl1(), new Impl2(), new Impl3() });
结果数组表达式仅使用所有找到的服务生成一次,如果之后添加新服务,或者更新或删除现有服务,则不会更改。您可以使用下面解释的 LazyEnumerable 来解决此限制。
4. 自定义封装器
要注册您自己的包装器,只需将设置参数指定为Setup.Wrapper 或Setup.WrapperWith :
void Main()
{
var container = new Container();
container.Register<ICmd, X>();
container.Register<ICmd, Y>();
container.Register(typeof(MenuItem<>), setup: Setup.Wrapper);
Console.WriteLine(container.IsRegistered(typeof(MenuItem<>), factoryType: FactoryType.Wrapper));
var items = container.Resolve<MenuItem<ICmd>[]>();
items.Dump();
}
public interface ICmd { }
public class X : ICmd { }
public class Y : ICmd { }
public class MenuItem<TCmd> where TCmd : ICmd { }
包装器和非包装器之间的主要区别在于 ResolveMany 和集合包装器如何处理它们:
- 当MenuItem正常注册时,数组将包含单个项目,因为单个MenuItem注册
- 当 MenuItem 注册为包装器时,它会以特殊方式处理:解析器将首先尝试查找所有包装类型 (ICmd),然后将每个找到的类型包装在MenuItem 中。
4.1 非泛型包装器
void Main()
{
{
var container = new Container();
container.Register<IService, Foo>();
container.Register<IService, Bar>();
container.Register<MyWrapper>(setup: Setup.Wrapper);
Console.WriteLine(container.IsRegistered(typeof(MyWrapper), factoryType: FactoryType.Wrapper));
var items = container.Resolve<MyWrapper[]>(requiredServiceType: typeof(IService));
items.Dump();
}
{
var container = new Container();
container.Register<IService, Foo>();
container.Register<IService, Bar>();
container.Register<MyWrapper<IService>>(setup: Setup.Wrapper);
Console.WriteLine(container.IsRegistered(typeof(MyWrapper<IService>), factoryType: FactoryType.Wrapper));
var items = container.Resolve<MyWrapper<IService>[]>();
items.Dump();
}
}
interface IService { }
class Foo : IService { }
class Bar : IService { }
class MyWrapper { public MyWrapper(IService service) { } }
class MyWrapper<T> { public MyWrapper(T service) { } }
|