Skip to content

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