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