关于Autofac 使用说明

当需要灵活且功能强大的DI组件时,考虑使用autofac。 
```c
var builder = new ContainerBuilder();
builder.RegisterType().As();
var container = builder.Build();
using (var scope = container.BeginLifetimeScope())
{
    var service = container.Resolve();
    var result = service.GetList();
}
```
 
## 一、注册
- 注册的为组件,暴露的为服务
- 每个组件暴露一个或多个服务,使用 As()方法连接起来
- 注册组件只能解析组件,不能使用服务去解析。注册组件且暴露服务后,服务将覆盖组件的解析。
例如:
```c
builder.RegisterType();
scope.Resolve();
scope.Resolve();//错误不能解析
```
- 默认情况下暴露多个服务后,由最后一个服务覆盖。可使用.AsSelf()使得一起可用。
- 暴露的相同的服务,则由最后一个覆盖。可通过PreserveExistingDefaults指定默认服务提供。

### 1.1 接口注册
```c
builder.RegisterType().As();
```

### 1.2 实例注册(已有对象)
```c
var output = new StringWriter();
builder.RegisterInstance(output).As();
builder.RegisterInstance(output).As().ExternallyOwned();//需考虑释放,若需自己控制生命周期则
```

### 1.3 Lambda注册(灵活)
```c
builder.Register(c => new ConfigReader("mysection")).As();
//判断注入:
builder.Register((c, p) =>
{
  var accountId = p.Named("accountId");
  if (accountId.StartsWith("9"))
  {
    return new GoldCard(accountId);
  }
  return new StandardCard(accountId);
});
//第二个参数可选
var card = container.Resolve(new NamedParameter("accountId", "12345"));
```

### 1.4 反射注册
默认自动匹配最多参数的构造方法,必须是实体类。解析时仍然需要提供必要的参数。
```c
builder.RegisterType();
builder.RegisterType(typeof(ConfigReader));
```

指定构造函数:
```c
builder.RegisterType().UsingConstructor(typeof(ILogger), typeof(IConfigReader));
```

### 1.5 开放泛型注册
容器请求一个匹配的服务类型时, Autofac会找到对应的封闭类型的具体实现
```c
builder.RegisterGeneric(typeof(NHibernateRepository<>)).As(typeof(IRepository<>)).InstancePerLifetimeScope();
```
       
### 1.6 条件注册
```c
//IfNotRegistered 未注册IService则注册
builder.RegisterType().As().IfNotRegistered(typeof(IService));

//OnlyIf 只有注册了IServer才注册IManager
builder.RegisterType().As().OnlyIf(reg => reg.IsRegistered(new TypedService(typeof(IService))));
```

## 二、注册时传参(解析式传参也可以) 
### 2.1 NamedParameter - 通过名字匹配目标参数(常量)
```c
builder.RegisterType().As().WithParameter("configSectionName", "sectionName");
```

### 2.2 TypedParameter - 通过类型匹配目标参数 (需要匹配具体的类型)
```c
builder.RegisterType().As().WithParameter(new TypedParameter(typeof(string), "sectionName"));
```
   
### 2.3 ResolvedParameter - 复杂参数的匹配
```c
builder.RegisterType()
       .As()
       .WithParameter(
         new ResolvedParameter(
           (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
           (pi, ctx) => "sectionName"));
```

### 2.4 Lambda注册参数
```c
builder.Register((c, p) => new ConfigReader(p.Named("configSectionName"))).As();
var reader = scope.Resolve(new NamedParameter("configSectionName", "sectionName"));
```

## 三、属性注入
### 3.1 属性注入
```c
builder.Register(c => new A { B = c.Resolve() });
WithProperty(): builder.RegisterType().WithProperty("PropertyName", propertyValue);
```

### 3.2 循环依赖
支持循环依赖,指定激活后事件处理程序
```c
builder.Register(c => new A()).OnActivated(e => e.Instance.B = e.Context.Resolve());
```

循环依赖:
- 使属性依赖项可写
- 设置注册类型PropertiesAutowired
- 两种类型不能注册为InstancePerDependency,最好使用SingleInstance或InstancePerLifetimeScope
```c
cb.RegisterType().InstancePerLifetimeScope().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
cb.RegisterType().InstancePerLifetimeScope().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
```

构造函数/属性依赖:
例如:A构造函数需要B,B属性包含A。此方式解决方式:
- 使属性依赖项可写
- 使构造函数属性注入的类型PropertiesAutowired
- 两种类型不能注册为InstancePerDependency,最好使用SingleInstance或InstancePerLifetimeScope
- **不支持构造函数相互依赖,DynamicProxy2创意编码解决?**
```c
//DependsByCtor的构造函数注入DependsByProp
cb.RegisterType().InstancePerLifetimeScope();
cb.RegisterType().InstancePerLifetimeScope().PropertiesAutowired(PropertyWiringOptions.AllowCircularDependencies);
```

### 3.3 方法注入(调用方法设置组件的值)
```c
//Lambda:
builder.Register(c => {
  var result = new MyObjectType();
  var dep = c.Resolve();
  result.SetTheDependency(dep);
  return result;
});
//激活时事件处理程序:
builder.Register()
  .OnActivating(e => {
    var dep = e.Context.Resolve();
    e.Instance.SetTheDependency(dep);
  });
```

## 四、程序集扫描
### 4.1 扫描类型
扫描类型主要用于指定规则注册一组类型,从程序集中进行加载
```c
//以Repository结尾的类型集合
var assembly = Assembly.GetExecutingAssembly();
builder.RegisterAssemblyTypes(dataAccess).Where(t => t.Name.EndsWith("Repository")).AsImplementedInterfaces();
```

类型过滤方式,如Where、PublicOnly(公共方法)、Except<>(表达式)排除指定类型或指定表达式的类型。
指定服务:
```c
builder.RegisterAssemblyTypes(asm)
       .Where(t => t.Name.EndsWith("Repository"))
       .As();
```

额外的注册方法:
- AsImplementedInterfaces(),将类型注册为将其所有公共接口作为服务提供(不包括IDisposable)
- AsClosedTypesOf(),注册关闭实例的开放泛型类型。
- AsSelf(),默认,将类型注册为它们自己

### 4.2 扫描模块
注册AModule需继承自Module,重写Load方法
```c
var assembly = typeof(AComponent).Assembly;
var builder = new ContainerBuilder();
builder.RegisterAssemblyModules(assembly);
builder.RegisterAssemblyModules(assembly); //限定类型

RegisterModule //可直接进行注册
public class AModule : Module
{
  protected override void Load(ContainerBuilder builder)
  {
    builder.Register(c => new Car(c.Resolve())).As();
  }
}
```


***IIS 回收AppDomain时,程序集按需加载,使用以下方式强制加载:***
***var assemblies = BuildManager.GetReferencedAssemblies().Cast(); ***

## 五、服务解析(推荐在生命周期内解析组件)
### 5.1 服务解析方法
当仅注册组件时,则用组件解析。当注册服务时,使用服务进行解析。
```c
var builder = new ContainerBuilder();
builder.RegisterType().As();
var container = builder.Build();
using(var scope = container.BeginLifetimeScope())
{
  var service = scope.Resolve();
}

//尝试解析,解析失败则还是会异常,没什么卵用
var service = scope.ResolveOptional();
IProvider provider = null;
if(scope.TryResolve(out provider))
{
}
```
### 5.2 服务解析传参
服务解析传参主要是注册时并不知道或不想传递参数值,故解析时候进行传递
服务解析传参与服务注册传参相同 跳转到[二、注册时传参](#registerPara)
```c
var reader = scope.Resolve(new NamedParameter("configSectionName", "sectionName"));
var reader = scope.Resolve(new NamedParameter("configSectionName", "sectionName"));

scope.Resolve(
    new NamedParameter("id", "service-identifier"),
    new TypedParameter(typeof(Guid), Guid.NewGuid()),
    new ResolvedParameter(
      (pi, ctx) => pi.ParameterType == typeof(ILog) && pi.Name == "logger",
      (pi, ctx) => LogManager.GetLogger("service")));
                  
```

### 5.1 隐式关系类型
作用:支持组件与服务之间特殊关系
- 直接依赖,A 构造函数依赖B,注册A,注册B,解析A即可。
- 延迟实例化,非频繁使用或较大代价使用延迟依赖
- 可控生命周期,不在需要则所有者释放。 
- 动态实例化,也就是支持动态构造,以及在构造的时候需要进行控制
- 带参数实例化,对象构造方法需要额外参数
- 可遍历型,相同服务多个实现
- 元数据审查,可以构造依赖时通过Meta传递任何数据
- 键控服务的查找,服务多实现根据键查找

```c
public class A
{
  Lazy _b; 
  //Owned _b; 可控生命周期
  //Func _newB; 动态实例化
  //Func _newB; 带参数实例化
  //Meta _b;元数据
  //IIndex _b;键控服务查找
  public A(Lazy b) { _b = b }
  public void M()
  {
      _b.Value.DoSomething();
  }
}

//可遍历型(IEnumerable, IList, ICollection)
public class MessageProcessor
{
  private IEnumerable _handlers;

  public MessageProcessor(IEnumerable handlers)
  {
    this._handlers = handlers;
  }

  public void ProcessMessage(Message m)
  {
    foreach(var handler in this._handlers)
    {
      handler.HandleMessage(m);
    }
  }
}

var builder = new ContainerBuilder();
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType().As();
builder.RegisterType();
var container = builder.Build();

using(var scope = container.BeginLifetimeScope())
{
  var processor = scope.Resolve();
  processor.ProcessMessage(m);
  
  //未注册报错
  //scope.Resolve();
  
  //未注册返回空
  //scope.Resolve>();
}
```

## 六、生命周期
生命周期作用域是可嵌套的并且它们控制了组件如何共享。追踪可释放对象并且当生命周期作用域被释放同时释放它们
注意:永远从一个生命周期作用域而不是从根容器中解析服务。另外注意单例存在于根作用域,在单例中依赖其他作用域的实例时,由于作用域或顺序将无法解析。
原则:避免非人为多个实例类型依赖一个单例,因为可能单例持有时间太长
```c
using(var scope = container.BeginLifetimeScope()){}//可进行内联

//标识的生命周期域。打了标识的生命周期域中的子域中可以共享父级域中的实例。继承层次中未找到,则会抛出异常
builder.RegisterType().As().InstancePerMatchingLifetimeScope("transaction");
using(var transactionScope = container.BeginLifetimeScope("transaction"))

//生命周期作用域内添加注册,用于方便在当前作用域添加注册而不对外暴露
using(var scope = container.BeginLifetimeScope(
  builder =>
  {
    builder.RegisterType().As();
    builder.RegisterModule();
  })){}
```
### 6.1 实例的作用域
实例作用域共有7种定义方式:
- 每个依赖一个实例,对每一个依赖或每一次调用创建一个新的唯一的实例。这也是默认的创建实例的方式。
```c
//两种方式均一样
builder.RegisterType();
builder.RegisterType().InstancePerDependency();
```

- 单例,在根作用域和所有嵌套作用域内均返回同一实例
```c
builder.RegisterType().SingleInstance();
```

- 生命周期作用域一个实例,在一个生命周期域中,每一个依赖或调用创建一个单一的共享的实例,且每一个不同的生命周期域,实例是唯一的不共享
```c
builder.RegisterType().InstancePerLifetimeScope();
using(var scope1 = container.BeginLifetimeScope())
{
   //解析Worker均为同一实例
}
```

- 标签作用域一个实例,通过打标签方式,精准控制每个组件在作用域的单例
标签相同,但不在一个作用域内,组件将不同
```c
builder.RegisterType().InstancePerMatchingLifetimeScope("myrequest");
using(var scope1 = container.BeginLifetimeScope("myrequest"))
{
    //解析Worker均为同一实例
}
```

- 每个请求一个实例,每个请求一个实例建立于每个匹配生命周期一个实例之上。
Asp.net core 使用生命周期作用域一个实例。实际需要依赖于具体的Asp.net Core对象
```c
var builder = new ContainerBuilder();
builder.RegisterType().InstancePerRequest();
```
- 每次被拥有一个实例,简单说就是将依赖作用域绑定到拥有它的实例上
```c
//ServiceForHandler 服务将会绑定上它拥有的 MessageHandler
var builder = new ContainerBuilder();
builder.RegisterType();
builder.RegisterType().InstancePerOwned();
```
- 线程作用域,一个线程一个作用域。多线程场景中, 父级作用域不能在派生出的线程下被释放,若子线程中释放父级作用域, 则组件不能解析。
ILifetimeScope 父级的生命周期作用域注入到派生线程的代码中
```c
builder.RegisterType().InstancePerLifetimeScope();
```

## 七、组件释放
### 7.1 自动释放依赖于作用域生命周期,自动释放可通过实现IDisposable进行释放。也可以实习IAsyncDisposable 进行异步释放。
```c
class MyComponent : IDisposable, IAsyncDisposable
{
  INetworkResource myResource;
  public void Dispose()
  {
    myResource.Close();
  }
  public async ValueTask DisposeAsync()
  {
    await myResource.CloseAsync();
  }
}
```

### 7.2 手动释放
```c
特定释放方式,用于同时需要调用其他释放方法:
builder.RegisterType().OnRelease(instance => instance.CleanUp());

禁止释放:
builder.RegisterType().ExternallyOwned();
```

### 7.3 生命周期事件
- OnPreparing,被需要时触发
- OnActivating,使用前触发。可进行修改实例包装或属性方法注入,或初始化工作。
```c
builder.Register(c => c.Resolve()).OnActivating
```
- OnActivated,组件构造完成后触发
- OnRelease,事件替换了 原始的组件释放行为

## 八、配置与项目介入
### Asp.net core
Asp.net core 3.1+ 接入方式,
```c
Program.cs
public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory()) //主要是这行
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup();
        });
        
Startup.cs
public ILifetimeScope AutofacContainer { get; private set; }
//注入模块
public void ConfigureContainer(ContainerBuilder builder)
{
    builder.RegisterModule();
}
//模块定义
protected override void Load(ContainerBuilder builder)
{
    //注册Service中的对象,Service中的类要以Service结尾,否则注册失败
    builder.RegisterAssemblyTypes(GetAssemblyByName("Autofac.Service")).
        Where(a => a.Name.EndsWith("Service") && typeof(IService).IsAssignableFrom(a)).AsImplementedInterfaces();
    //注册Repository中的对象,Repository中的类要以Repository结尾,否则注册失败
    builder.RegisterAssemblyTypes(GetAssemblyByName("Autofac.Repository")).
        Where(a => a.Name.EndsWith("Repository") && typeof(IRepository).IsAssignableFrom(a)).AsImplementedInterfaces();
}        
```


 

你可能感兴趣的:(Autofac,.net)