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
官方文档进行深入了解。