IoC之AutoFac(一)——简单使用和组件注册

阅读目录


一、AutoFac简单使用

复制代码
 1 namespace AutofacDemo
 2 {
 3     class Program
 4     {
 5         //用于存储容器的属性
 6         private static IContainer Container { get; set; }
 7         static void Main(string[] args)
 8         {
 9             //获取容器
10             var builder = new ContainerBuilder();
11             //注册组件(类),并公开服务(接口)
12             builder.RegisterType<ConsoleOutput>().As<IOutput>();
13             builder.RegisterType<TodayWriter>().As<IDateWriter>();
14             //存储容器
15             Container = builder.Build();
16 
17             WriteDate();//输出当前的日期
18         }
19         public static void WriteDate()
20         {
21             using (var scope=Container.BeginLifetimeScope())
22             {
23                 //解析组件(功能类似于new一个IDateWriter的实现类)
24                 var writer = scope.Resolve<IDateWriter>();
25                 writer.WriteDate();
26             }
27         }
28     }
29     //输出功能接口
30     public interface IOutput
31     {
32         void Write(string content);
33     }
34    //控制台输出类
35     public class ConsoleOutput : IOutput
36     {
37         public void Write(string content)
38         {
39             Console.WriteLine(content);
40         }
41     }
42     //输出日期的接口
43     public interface IDateWriter
44     {
45         void WriteDate();
46     }
47    
48    //输出今天日期的类
49     public class TodayWriter : IDateWriter
50     {
51         private IOutput _output;//输出日期的类依赖IOutput
52         public TodayWriter(IOutput output)
53         {
54             this._output = output;
55         }
56         public void WriteDate()
57         {
58             this._output.Write(DateTime.Today.ToShortDateString());
59         }
60     }
61 }
复制代码

应用程序执行过程:

  “WriteDate”方法向Autofac询问IDateWriter。

  Autofac看到IDateWriter映射到TodayWriter,所以开始创建一个TodayWriter。

  Autofac认为TodayWriter在其构造函数中需要一个IOutput。

  Autofac看到IOutput映射到ConsoleOutput,所以创建一个新的ConsoleOutput实例。

  Autofac使用新的ConsoleOutput实例来完成TodayWriter的构建。

  Autofac将“WriteDate”的完全构建的TodayWriter返回给消费者。

  之后,如果您希望应用程序编写不同的日期,则可以实现不同的IDateWriter,然后在应用程序启动时更改注册。你不必改变任何其他类。 这样就实现了反转控制!


二、注册

      使用Autofac 注册组件的任务是:通过创建一个ContainerBuilder并且告知builder 哪些组件公开哪些服务。

2.1 注册方式

复制代码
 1  // 创建组件/服务注册的容器
 2             var builder = new ContainerBuilder();
 3 
 5             ////----------------------一、反射组件----------------////
 6             // 反射组件方法1---通过类型注册
 7             builder.RegisterType<ConsoleOutput>().As<IOutput>();
 8             // 反射组件方法2---通过构造函数注册
 9             builder.RegisterType<TodayWriter>()
10                    .UsingConstructor(typeof(IOutput)).As<IDateWriter>();
11             
12      
13 
14             ////--------------------二、实例组件----------------------////
15             // 注册创建的对象实例,把该实例作为一个服务
16             var output = new ConsoleOutput();
17             builder.RegisterInstance(output).As<IOutput>();
18             //不影响系统中原有的单例
19             builder.RegisterInstance(MySingleton.GetInstance()).ExternallyOwned();  
20 
21             
22 
23             ////------------------三、Lambda表达式组件---------------////
24             //参数c是组件上下文(一个IComponentContext对象) ,此处该组件被创建。您可以使用它来解析来自于容器的其他组件,从而帮助你创建组件。
25 
26             builder.Register(c => new TodayWriter(c.Resolve<IOutput>())).As<IDateWriter>();
27 
28             //分离组件创建最大的好处就是具体类型可以变化
29             //一个组件分离的Demo:注册一个信用卡组件
30             builder.Register<CreditCard>(
31                 (c, p) =>
32                 {
33                     var accountId = p.Named<string>("accountId");
34                     if (accountId.StartsWith("9"))
35                     {
36                         return new GoldCard(accountId);
37                     }
38                     else
39                     {
40                         return new StandardCard(accountId);
41                     }
42                 });
43             //根据参数不同获取不同种类的信用卡类实例
44             var card = container.Resolve<CreditCard>(new NamedParameter("accountId", "12345"));
45 
46 
47             ////-------------------------四、泛型注册------------------////
48             //泛型注册,可以通过容器返回List<T> 如:List<string>,List<int>等等
49             builder.RegisterGeneric(typeof(List<>)).As(typeof(IList<>)).InstancePerLifetimeScope();
50             using (IContainer container = builder.Build())
51             {
52                 IList<string> ListString = container.Resolve<IList<string>>();
53             }
54 
55 
56             ////-----------------------五、服务与组件-------------------////
57             builder.RegisterType<ConsoleLogger>();//默认把注册类型作为公开服务
58             builder.RegisterType<ConsoleLogger>().As<ILogger>().As<IMyLogger>();//可以公开任意多个服务,但是默认的注册类型会被覆盖
59             builder.RegisterType<ConsoleLogger>().AsSelf().As<ILogger>();//加上AsSelf()默认的注册类型不会被覆盖
60             //当公开的服务重名时,服务使用的默认组件是后注册的那个
61             builder.Register<ConsoleLogger>().As<ILogger>();
62             builder.Register<FileLogger>().As<ILogger>();//默认使用后注册的FileLogger
63             //阻止该默认行为,使用builder.Register<FileLogger>().As<ILogger>().PreserveExistingDefaults(),这样不会覆盖以前注册的组件
64 
65 
66             ////-----------六、条件注册(IfNotRegistered,OnlyIf)-------////
67             //HandlerC不会被注册
68             builder.RegisterType<HandlerA>()
69                     .AsSelf()
70                     .As<IHandler>()
71                     .IfNotRegistered(typeof(HandlerB));
72             builder.RegisterType<HandlerB>()
73                    .AsSelf()
74                    .As<IHandler>();
75             builder.RegisterType<HandlerC>()
76                    .AsSelf()
77                    .As<IHandler>()
78                    .IfNotRegistered(typeof(HandlerB));
79 
80             //只有IService和HandlerB都注册时,才会去注册Manager
81             builder.RegisterType<Manager>()
82                    .As<IManager>()
83                    .OnlyIf(reg =>
84                      reg.IsRegistered(new TypedService(typeof(IService))) &&
85                      reg.IsRegistered(new TypedService(typeof(HandlerB))));
86 
87 
88             // 编译容器完成注册且准备对象解析
89              Container = builder.Build();
90 
91             // 现在你可以使用 Autofac 解析服务. 例如,这行将执行注册的lambda表达式对于 IConfigReader 服务.
92              using (var scope = Container.BeginLifetimeScope())
93              {
94                  var writer = Container.Resolve<IOutput>();
95                  var dataWriter = Container.Resolve<IDateWriter>();
96                  writer.Write("111");//输出111
97                  dataWriter.WriteDate();//输出当前日期
98                  Console.ReadKey();
99              }
复制代码

2.2 带参数注册

1、参数类型

  Autofac支持的参数类型有三种:

    NamedParameter - 通过名称匹配目标参数

    TypedParameter - 通过类型匹配目标参数(需要精确匹配类型)

    ResolvedParameter - 灵活的参数匹配

  例子:一个读取配置文件的类,构造器需要传入参数:节点名称

复制代码
1 public class ConfigReader : IConfigReader
2     {
3         public ConfigReader(string configSectionName)
4         {
5             // 存储配置的节点名称
6         }
7 
8         // ...读取基于节点名称的配置
9     }
复制代码

2、反射组件参数

Parameters with Reflection Components----注册时传入

复制代码
 1 // 使用一个命名参数:
 2 builder.RegisterType<ConfigReader>()
 3        .As<IConfigReader>()
 4        .WithParameter("configSectionName", "mySectionName");
 5 
 6 // 使用一个类型参数:
 7 builder.RegisterType<ConfigReader>()
 8        .As<IConfigReader>()
 9        .WithParameter(new TypedParameter(typeof(string), "mySectionName"));
10 
11 // 使用一个解析参数:
12 builder.RegisterType<ConfigReader>()
13        .As<IConfigReader>()
14        .WithParameter(
15          new ResolvedParameter(
16            (pi, ctx) => pi.ParameterType == typeof(string) && pi.Name == "configSectionName",
17            (pi, ctx) => "mySectionName"));
复制代码

3、Lambda表达式组件参数

Parameters with Lambda Expression Components----解析时传入

  在组件注册表达式中,你可以通过改变委托签名使用传入参数进行注册,代替仅仅接收一个IComponentContext参数,一个IComponentContext 和 IEnumerable<Parameter>参数:
1 builder.Register((c, p) =>
2                  new ConfigReader(p.Named<string>("configSectionName")))
3        .As<IConfigReader>();

 当你解析参数时,你的lambda将使用这些参数来传入值:

 var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "mySectionName"));

 参考文章:

  1、https://blog.csdn.net/chiyueqi/article/details/52446569

  2、http://www.yuanjiaocheng.net/Autofac/register-parameters.html

阅读目录


一 Resolve方法

在您的组件注册了适当的服务后,您可以从内置的容器和子生命周期范围中解析服务。 您可以使用Resolve()方法,还是使用上篇的例子:

复制代码
 1     private static IContainer Container { get; set; }
 2         static void Main(string[] args)
 3         {
 4             var builder = new ContainerBuilder();
 5             //注册服务
 6             builder.RegisterType<ConsoleOutput>().As<IOutput>();
 7             Container = builder.Build();
 8             //解析服务
 9             using (var scope=Container.BeginLifetimeScope())
10             {
11                var output= scope.Resolve<IOutput>();
12                output.Write("outputsomething");
13                Console.ReadKey();
14             }  
15         }
复制代码

  解析服务时,Autofac将自动链接服务的整个依赖关系层次,并解析完全构建服务所需的任何依赖关系。 如果您的循环依赖关系被错误地处理,或者缺少必需的依赖关系,那么您将得到一个DependencyResolutionException。

二 TryResolve和ResolveOptional方法

如果您有可能注册或可能不被注册的服务,您可以使用ResolveOptional()或TryResolve()来尝试对服务进行有条件解决:

复制代码
 1                 //解析服务
 2                 using (var scope = Container.BeginLifetimeScope())
 3                 {
 4                     //1.ResolveOptional:IOutput注册的话解析,未被注册返回null
 5                     var service = scope.ResolveOptional<IOutput>();
 6 
 7                     //2.TryResolve:IOutput注册的话解析获取一个类型实例,未注册返回null
 8                     IOutput output = null;
 9                     //如果有IOutPut服务,执行输出
10                     if (scope.TryResolve<IOutput>(out output))
11                     {
12                         output.Write("outputsomething");
13                     }
14                     Console.ReadKey();
15                 }
复制代码

三 解析服务时传参

  解析服务的时候,您可能会发现需要将参数传递给Autofac。 (如果您在注册时知道值,则可以在注册中提供它们,详细见上篇。)Resolve()方法使用可变长度的参数列表在注册时接受相同的参数类型

3.1 可用参数类型

Autofac提供了几种不同的参数匹配策略:

  • NamedParameter - 按名称匹配目标参数

  • TypedParameter - 按类型匹配目标参数(需要精确类型匹配)

  • ResolvedParameter - 灵活的参数匹配

NamedParameter和TypedParameter只能提供常量值。

ResolvedParameter可以用作提供从容器动态检索的值的方法,例如。 通过名称解析服务。

3.2 带反射组件的参数

      当您解析基于反射的组件时,类型的构造函数可能需要您需要根据运行时值指定的参数,这在注册时不可用。 您可以在Resolve()方法调用中使用一个参数来提供该值。

  假设您有一个配置读取器,需要传递一个配置部分名称:

复制代码
1  public class ConfigReader : IConfigReader
2     {
3         public ConfigReader(string configSectionName)
4         {
5             // 存储配置的节点名称
6         }
7 
8         // 读取基于节点名称的配置
9     }
复制代码

  您可以将参数传递给Resolve()调用,如下所示:

//注册
 builder.RegisterType<ConfigReader>().As<IConfigReader>();
//解析
 var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "mySectionName"));

  如果您有多个参数,只需通过Resolve()方法将它们全部传递:

 var service = scope.Resolve<AnotherService>(
                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")));

3.3 具有Lambda表达式组件的参数

  使用lambda表达式组件注册,您需要在lambda表达式中添加参数处理,因此当Resolve()调用传入时,可以利用它们。

在组件注册表达式中,您可以通过更改用于注册的代理签名来使用传入参数。 而不是仅仅使用IComponentContext参数,而是接收一个IComponentContext和一个IEnumerable <Parameter>:

相关实例:

   // c 是当前组件上下文
   // p 是IEnumerable<Parameter>参数集合
   builder.Register((c, p) =>new ConfigReader(p.Named<string>("configSectionName")))
          .As<IConfigReader>();

现在,当您解析IConfigReader时,您的lambda将使用传递的参数:

//解析时传参
var reader = scope.Resolve<IConfigReader>(new NamedParameter("configSectionName", "sectionName"));
原文地址:https://www.cnblogs.com/Alex80/p/13207171.html