1、前言
前面的博客主要介绍了Autofac中的一些注册方法,下面就来介绍一下Autofac中实例的生命周期。之前在介绍ASP.NET Core内置IoC容器的时候说过,实例的生命周期有:瞬时生命周期、域生命周期、全局单例生命周期,而Autofac在这三种周期之上又新增了若干周期模式,下面开始介绍。
2、Autofac中的生命周期
2.1、InstancePerDependency
InstancePerDependency表示瞬时生命周期,它是Autofac中默认的周期模式,在瞬时生命周期中,容器每次都会创建新的实例,代码如下:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerDependency();
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj1 = scope.Resolve<IAnimal>();
IAnimal obj2 = scope.Resolve<IAnimal>();
Console.WriteLine(obj1.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
}
Console.WriteLine("---------");
// scope2
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj3 = scope.Resolve<IAnimal>();
IAnimal obj4 = scope.Resolve<IAnimal>();
Console.WriteLine(obj3.GetHashCode());
Console.WriteLine(obj4.GetHashCode());
}
}
}
}
运行结果如下所示:
43527150
56200037
---------
36038289
55909147
可以发现:obj1、obj2、obj3、obj4的哈希值均不相同,因此它们是不同的实例。
2.2、InstancePerLifetimeScope
InstancePerLifetimeScope表示域生命周期。域周期表示在同一个域中,每个实例都是相同的,而不同域中的实例又是不同的,因此可以理解为在域中实现了单例模式,代码如下:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerLifetimeScope();
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj1 = scope.Resolve<IAnimal>();
IAnimal obj2 = scope.Resolve<IAnimal>();
Console.WriteLine(obj1.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
}
Console.WriteLine("---------");
// scope2
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj3 = scope.Resolve<IAnimal>();
IAnimal obj4 = scope.Resolve<IAnimal>();
Console.WriteLine(obj3.GetHashCode());
Console.WriteLine(obj4.GetHashCode());
}
}
}
}
运行结果如下所示:
43527150
43527150
---------
56200037
56200037
可以发现:obj1与obj2的哈希值相同,obj3与obj4的哈希值相同,因此obj1与obj2为同一实例,obj3与obj4为同一实例。
2.3、InstancePerMatchingLifetimeScope
InstancePerMatchingLifetimeScope也表示域生命周期,它允许开发者做更精细的控制,那么它与InstancePerLifetimeScope的区别在哪里?来看下面一段代码:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerLifetimeScope();
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj1 = scope.Resolve<IAnimal>();
IAnimal obj2 = scope.Resolve<IAnimal>();
Console.WriteLine(obj1.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
Console.WriteLine("---------");
// 子域
using (var scope1 = scope.BeginLifetimeScope())
{
IAnimal obj3 = scope1.Resolve<IAnimal>();
IAnimal obj4 = scope1.Resolve<IAnimal>();
Console.WriteLine(obj3.GetHashCode());
Console.WriteLine(obj4.GetHashCode());
}
}
}
}
}
上面的代码设置为InstancePerLifetimeScope模式,在域scope中又创建了一个子域scope1,运行结果如下:
43527150
43527150
---------
56200037
56200037
可以发现:子域中的obj3和obj4的哈希值相同,但它们与obj1和obj2的哈希值却不相同。因此obj1与obj2为同一实例,obj3与obj4为同一实例。现在切换为InstancePerMatchingLifetimeScope模式,代码如下:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerMatchingLifetimeScope("abc");
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope("abc"))
{
IAnimal obj1 = scope.Resolve<IAnimal>();
IAnimal obj2 = scope.Resolve<IAnimal>();
Console.WriteLine(obj1.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
Console.WriteLine("---------");
// 子域
using (var scope1 = scope.BeginLifetimeScope())
{
IAnimal obj3 = scope1.Resolve<IAnimal>();
IAnimal obj4 = scope1.Resolve<IAnimal>();
Console.WriteLine(obj3.GetHashCode());
Console.WriteLine(obj4.GetHashCode());
}
}
}
}
}
上面的代码在注册时打了一个标签,名称为abc,然后通过该标签名称创建域scope,运行结果如下所示:
43527150
43527150
---------
43527150
43527150
可以发现:obj1、obj2、obj3、obj4的哈希值均相同,它们为同一实例。因此可以这样理解:如果将生命周期设置为InstancePerMatchingLifetimeScope模式,那么在该域中,不管创建了多少子域,它们都会调用同一实例。
2.4、InstancePerRequest
InstancePerRequest表示在每次HTTP请求内实现单例。该周期模式只适用于Web项目,由于本文的例子基于控制台程序,因此不太方便举例说明。本质上这也是一种域内单例的周期模式,但并不经常使用。
2.5、InstancePerOwned
关于InstancePerOwned周期,相关的介绍不多,我在这里就凭借自己的一点理解来进行说明,如果有不对的地方还请大家指出来。首先新增一个类Zoo,代码如下:
namespace App
{
public class Zoo
{
protected readonly IAnimal _animal;
public Zoo(IAnimal animal)
{
_animal = animal;
}
public string Get()
{
return _animal.GetMsg();
}
}
}
然后在Autofac中注册该类,代码如下:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Zoo>();
builder.RegisterType<Cat>().As<IAnimal>();
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope())
{
var cat = scope.Resolve<IAnimal>();
Console.WriteLine(cat.GetMsg());
var zoo = scope.Resolve<Zoo>();
Console.WriteLine(zoo.Get());
}
}
}
}
运行结果如下所示:
This is cat
This is cat
上面这段代码很简单,现在把代码改一下,把InstancePerOwned周期加进来:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Zoo>();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();
// 构建容器
IContainer container = builder.Build();
// scope
using (var scope = container.BeginLifetimeScope())
{
var cat = scope.Resolve<IAnimal>();
Console.WriteLine(cat.GetMsg());
var zoo = scope.Resolve<Zoo>();
Console.WriteLine(zoo.Get());
}
}
}
}
运行代码,程序报错,发现在创建Cat实例的时候发生异常,如下图所示:

这是因为InstancePerOwned模式会对Cat实例的创建做出限制:
builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();
根据我对官方文档的理解,上面这行代码可以理解为:Cat实例的创建依赖于Zoo实例的创建。当Zoo实例被创建后,容器会自动创建Cat实例,同时在域scope内无法单独使用Cat实例,它只能在Zoo实例的内部进行调用。因此开发者无法在域scope内手动调用Resolve方法生成Cat实例。下面修改一下代码:
using Autofac;
using Autofac.Features.OwnedInstances;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Zoo>();
builder.RegisterType<Cat>().As<IAnimal>().InstancePerOwned<Zoo>();
// 构建容器
IContainer container = builder.Build();
// scope
using (var scope = container.BeginLifetimeScope())
{
var zoo = scope.Resolve<Owned<Zoo>>();
Console.WriteLine(zoo.Value.Get());
zoo.Dispose();
}
}
}
}
运行结果如下所示:
This is cat
在使用InstancePerOwned周期时,我们需要使用Owned<>来接收容器创建的实例,然后对Value属性进行操作。在上面的代码中,当调用Resolve<Owned<Zoo>>时,Cat实例就会被自动创建。但无法直接调用。对象使用完后需要手动调用Dispose方法对其进行销毁。
2.6、SingleInstance
SingleInstance表示全局单例生命周期,这个比较好理解,即:所有创建的实例均为同一实例,代码如下:
using Autofac;
using System;
namespace App
{
internal class Program
{
static void Main(string[] args)
{
// 注册接口和类
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Cat>().As<IAnimal>().SingleInstance();
// 构建容器
IContainer container = builder.Build();
// scope1
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj1 = scope.Resolve<IAnimal>();
IAnimal obj2 = scope.Resolve<IAnimal>();
Console.WriteLine(obj1.GetHashCode());
Console.WriteLine(obj2.GetHashCode());
}
Console.WriteLine("---------");
// scope2
using (var scope = container.BeginLifetimeScope())
{
IAnimal obj3 = scope.Resolve<IAnimal>();
IAnimal obj4 = scope.Resolve<IAnimal>();
Console.WriteLine(obj3.GetHashCode());
Console.WriteLine(obj4.GetHashCode());
}
}
}
}
运行结果如下所示:
43527150
43527150
---------
43527150
43527150
3、结语
本文主要介绍了Autofac中实例的生命周期。一般情况下,瞬时、域、单例三种周期模式应用较多,而其他的周期模式则相对较少,有兴趣的同志可以查看Autofac官方文档进行深入了解。
最后
以上就是冷艳仙人掌最近收集整理的关于ASP.NET Core 3.1系列(26)——Autofac中的实例生命周期的全部内容,更多相关ASP.NET内容请搜索靠谱客的其他文章。
发表评论 取消回复