zoukankan      html  css  js  c++  java
  • 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"));
  • 相关阅读:
    Yii CGridView 之 SQL 语句
    Yii的srbac拓展中“用户已经获授权项”无法查看
    MyBatis通过注解实现映射中的嵌套语句和嵌套结果
    MyBatis一对多映射简单查询案例(嵌套结果)
    MyBatis绑定Mapper接口参数到Mapper映射文件sql语句参数
    将前端请求中的数据绑定到Spring MVC响应方法中参数的四种方法
    MyBatis一对多映射简单查询案例(嵌套Mapper映射文件中的sql语句)
    Spring MVC与html页面的交互(以传递json数据为例)
    使用Spring JDBC连接数据库(以SQL Server为例)
    spring mvc访问html页面404报错解决
  • 原文地址:https://www.cnblogs.com/Alex80/p/13207171.html
Copyright © 2011-2022 走看看