Autofac
https://autofaccn.readthedocs.io/zh/latest/index.html
注册
反射注册
指定类型后,使用反射方式进行实例化
// 注册类型,使用 Resolve 返回实例
builder.RegisterType (typeof (Item));
builder.RegisterType<Item> ();
builder.RegisterType<Item> ().As<IItem> (); // 注册接口类型, 如果实现多个接口可以使用多个 as 连接起来
// 创建实例
var item = container.Resolve<IItem> ();
// 创建实例,如果对象未注册 ,同则不会抛出异常
scope.ResolveOptional<IOptions>();
// 如果注册了接口,还要注册组件,则使用 AsSelf
builder.RegisterType<Item>().AsSelf().As<IItem>();
指定构造函数
// 可以在注册组件时指定调用对象的构造函数
// 如果构造函数中传入的对象也已注册过,则会自动构建并传入
// 如果有多个构造函数,则调用参数最多的那个
// 如果要调用指定的构造函数,则使用代码指定, 如下调用两个传入参数的构造函数
builder.RegisterType<MyComponent>()
.UsingConstructor(typeof(ILogger), typeof(IConfigReader));
实例组件
// 除了注册时指定单实例外,可以直接引用已经创建的对象作为单实例
IItem item = new Item();
builder.RegisterInstance(item).As<IItem>();
// 如果使用单实例注册外,可被自动释放
// 如果需要自行管理生存期的话,调用 ExternallyOwned
builder.RegisterInstance(item).As<IItem>().ExternallyOwned()
动态创建 (Lambda表达式组件)
// 可以动态的调用构造函数等,
builder.Register((ctx) => new Item(ctx.Resolve<ILogger>()));
通过参数生成实例
// 见 《传递参数》中的 5 部分
注册泛型对象
// 可以指定一个泛型对象的生成器
builder.RegisterGeneric(typeof(ItemType<>)).As<IItem>();
// 使用
container.Resolve<ItemType<ILogger>>();
注册默认对象
// 如果对同一个接口多次进行注册,生成时返回最后注册的那个
// 但是如果注册时,希望达到以前注册过就使用以前的。那么调用 PreserveExistingDefaults
builder.RegisterType<Item>().As<IItem>().PreserveExistingDefaults();
// 使用 IfNotRegistered , 如果接定的接口没有注册,则生效
builder.RegisterType<ServiceB>()
.As<IService>()
.IfNotRegistered(typeof(IService));
// OnlyIf 传入组件,调用组件函数进行注册是否生效判断
builder.RegisterType<Manager>()
.As<IManager>()
.OnlyIf(reg =>
reg.IsRegistered(new TypedService(typeof(IService))) &&
reg.IsRegistered(new TypedService(typeof(HandlerB))));
传递构造参数
// 比如 ConfigReader 构造需要传入参数
public class ConfigReader : IConfigReader
{
public ConfigReader(string configSectionName) { }
}
// 1. lambda 传参数
builder.Register(ctx => new ConfigReader("sectionName")).As<IConfigReader>();
// 2. 使用 named 参数
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter("configSectionName", "sectionName");
// 3. 使用指定类型的参数
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter(new TypedParameter(typeof(string), "sectionName"));
// 4. 使用动态生成
builder.RegisterType<ConfigReader>()
.As<IConfigReader>()
.WithParameter(
new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
(pi, ctx) => "sectionName"));
// 5. 生成时传入
// 5. 1 注册一个可以传入参数的生成回调函数
builder.Register(
(ctx, param) =>
{
// 返回指定属性值
var accountId = param.Named<string>(@"AccountId");
// 创建对象
return new Item(ctx.Resolve<ILogger>(), accountId);
});
// 5. 2 传入
var item = container.Resolve<Item>(new NamedParameter("accountId", "12345"));
属性注入
// 1. 使用 lambda 注入
builder.Register(ctx => new A { B = ctx.Resolve<B>() });
// 2. 使用 OnActivated 在生成对象后注入
builder.Register(c => new A()).OnActivated(e => e.Instance.B = e.Context.Resolve<B>());
// 3. 对属性对象自动创建
builder.RegisterType<A>().PropertiesAutowired();
// 4. 使用 WithProperty 指定
builder.RegisterType<A>().WithProperty("PropertyName", propertyValue);
方法注入
// 1. 使用 lambda 注入
builder.Register(c => {
var result = new MyObjectType();
var dep = c.Resolve<TheDependency>();
result.SetTheDependency(dep);
return result;
});
// 2. 使用 OnActivated 在生成对象后注入
builder
.Register<MyObjectType>()
.OnActivating(e => {
var dep = e.Context.Resolve<TheDependency>();
e.Instance.SetTheDependency(dep);
});
扫描程序程进行注册
// 具体见: https://autofaccn.readthedocs.io/zh/latest/register/scanning.html
// 1. 载入程序集
var dataAccess = Assembly.GetExecutingAssembly();
// 2. 使用 Autofac 指定一些条件进行扫描注册, 比如通过名称或是通过类型
builder.RegisterAssemblyTypes(dataAccess)
.Where(t => t.Name.EndsWith("Repository"))
.AsImplementedInterfaces();
解析服务
// 如果解析时缺少必要依赖或是不当循环,抛出 DependencyResolutionException
// 一般使用 Resolve 返回对象
var service = scope.Resolve<IService>();
// 如果没有注册,则返回 null
scope.ResolveOptional<IService>();
// 检测组件有没有注册
if(scope.TryResolve<IService>(out var provider)) { }
传入参数
// 注册时加入对参数的支持
builder.Register( (ctx, param) => { ... } );
// 解析时传入参数
scope.Resolve<IItem>(new NamedParameter(), new TypedParameter(), new ResolvedParameter());
// 使用 Func<T> 指定
var func = scope.Resolve<Func<int, int, string, DuplicateTypes>>();
解析依赖关系
// 默认情况下, 如果构造专入类型被注册,则自动生成构造函数对象并传入。并且自动管理生成周期
// 1. 比如 a 依赖 B
public class A { public A(B dependency) { ... } }
// 2. 可传入一个 Lazy<T> 对象, 使用时再调用,可以延迟实例化
public class A { public A(Lazy<B> dependency) { ... } }
// 3. 可传入 Owned<T>, 调用类似于 Lazy, 但是生存期由调用类控制
public class A {
public A(Lazy<B> dependency) { _b = dependency }
public void M()
{
// 就算所在 scope 配置为唯一实例,也不受影响, 直接另外生成一个实例对象
_b.Value.DoSomething();
_b.Dispose(); // 不用了直接释放,
}
}
// 4. 可传入 Func<T> , 生存期受全局控制
public class A { public A(Func<B> dependency) { ... } }
// 5. 可传入带参数 Func<T>, 即指定构造函数, 下例中 B 的构造函数要传入 T1, T2, T3
public class A { public A(Lazy<T1, T2, T3, B> dependency) { ... } }
// 6. 可以传入 Meta<T>, 或是 Lazy<T, M>(Meta<Lazy<B>, M>) 带有参数的元数据
builder.RegisterType<Item>().As<IItem>().WithMetadata("大将", "赵云");
public class A { public A(Meta<Item> dependency) { ... } }
// dependency.Metadata["大将"] == "赵云"
// 7. 多个相同服务,指定键 IIndex<keyT, T>, 指定一个键类型 kyeT 可以为字符串
// 7.1 为同一个接口指定不同的类型
builder.RegisterType<Item>().As<IItem>().Keyed<TaskStatus>(TaskStatus.Canceled);
builder.RegisterType<OtherItem>().As<IItem>().Keyed<TaskStatus>(TaskStatus.RanToCompletion);
// 7.2 使用 scope.Resolve, 或是构造函数传入 IIndex<>, 比如下面的
var index = scope.Resolve<IIndex<TaskStatus, IItem>>();
// 7.3 指定指定在类型,解析(创建)对象
var item = index[TaskStatus.Canceled];
传入构造参数
// 比如下例构造函数
public class DuplicateTypes { public DuplicateTypes(int a, int b, string c) { } }
// 注册一个与构造函数相同的类型
public delegate DuplicateTypes FactoryDelegate(int a, int b, string c);
// 注册类型生成工厂
builder.RegisterType<DuplicateTypes>();
builder.RegisterGeneratedFactory<FactoryDelegate>(new TypedService(typeof(DuplicateTypes)));
// 生成对象
var func = scope.Resolve<FactoryDelegate>();
var obj = func(1, 2, "three");
// 错误的方法
var func = container.Resolve<Func<int, int string, DuplicateTypes>>();
func(1, 2, "three"); // 会报错。因为参数中有相同类型的声明。可能是自动解析并传参数的, 有相同类型的话会失败
可遍历对象
public class MessageProcessor
// 所有注册的 IMessageHandler 都会被实例化传入
{ public MessageProcessor(IEnumerable<IMessageHandler> handlers) {} }
// 所有注册的 IMessageHandler 都会被实例化返回,如果没有注册,则返回空数组
scope.Resolve<IEnumerable<IMessageHandler>>();
生存期
var builder = new ContainerBuilder ();
// 注册 IItem 使用 Item 初始化, 每次 Resolve 返回一个新实例
builder.RegisterType<Item> ().As<IItem> ();
builder.RegisterType<Item> ().InstancePerDependency ();
// 生次返回同一个实例
builder.RegisterType<Item> ().SingleInstance ();
// 每个生存空间下只生成一个实例
builder.RegisterType<Item> ().InstancePerLifetimeScope ();
// 在同一个 container.BeginLifetimeScope("myrequest") 下生成一个实例
// 不同 container.BeginLifetimeScope("myrequest")) 下生成不同实例
// container.BeginLifetimeScope()) 下 scope.Resolve<Item>() 下抛出异常,因为没有定义
builder.RegisterType<Item> ().InstancePerMatchingLifetimeScope ("myrequest");
// web form 或 mvc 下。 在一个请求之间维护一个实例
// 应该会在不同的平台下做不同的初始化
builder.RegisterType<Item> ().InstancePerRequest ();
// 应该是一个 MessageHandler 下的 Item 为一个实例
builder.RegisterType<MessageHandler> ();
builder.RegisterType<Item> ().InstancePerOwned<MessageHandler> ();
var msg = scope.Resolve<Owned<MessageHandler>> ();
msg.Dispose (); // 必须自己释放
// 每线程中一个实例
// 在每个线程函数运行时调用 InstancePerLifetimeScope (); 那么在此作用域下,每次创建的实例就是唯一实例,从而达到每线程一个实例
在生存期中注册
container.BeginLifetimeScope((builder) => builder.RegisterType<ILogger>())
释放
所有创建的对象在作用域到期后自动释放(如果有 IDisposable ,自动调用 Dispose())。
异步释放
await using (var scope = container.BeginLifetimeScope())
{
// 生成的对象如果实现 IAsyncDisposable, 则 public async ValueTask DisposeAsync() 被调用
// 如果仅实现 IDisposable, 则 Dispose() 被调用
// 实现 IAsyncDisposable 的组件建议同时实现 IDisposable
}
回调释放
// 在 OnRelease 回调中释放,注意,重载后要调用 Dispose() , 如果有的话
builder.RegisterType<SomeComponent>()
.OnRelease(instance => instance.CleanUp());
禁止释放
// 使用 ExternallyOwned 指定不自动释放,手工控制
builder.RegisterType<SomeComponent>().ExternallyOwned();
// 构造函数参数中使用 Owned<T>, 见 《解析依赖关系》中相关部分
class B { B(Owned<ILogger> owner) }
事件
函数 | 说明 | |
---|---|---|
OnPreparing | 创建前准备一些参数 | |
OnActivating | 创建新实例,可以注入属性、其它初始化,或返回另外一个实例 OnActivating(e =>e.ReplaceInstance(item)) |
|
OnActivated | 实例创建后调用 | |
OnRelease | 组件释放时调用 |
启动
实现 IStartable
// 实例化时 IStartable.Start() 被调用
public class Item : IItem, IStartable
{
public void Start() {}
}
使用 AutoActivate
// 组件被自动初始化
builder.RegisterType<Item>().AutoActivate(); // 注意,AutoActivate 应该不能被 Resolve 创建
回调 RegisterBuildCallback
// 在 builder.Build() 时被创建
builder.RegisterBuildCallback(c => c.Resolve<DbContext>());
配置
https://autofaccn.readthedocs.io/zh/latest/configuration/index.html
与其它系统集成
```csharp // 微软的注入服务 var services = new ServiceCollection();
var builder = new ContainerBuilder(); builder.Populate(services); // 接管微软的注入服务 ```
https://autofaccn.readthedocs.io/zh/latest/integration/index.html