• C#下IOC/依赖注入框架Grace介绍


    对依赖注入或控制反转不了解的童鞋请先自行学习一下这一设计,这里直接介绍项目和实现步骤。

    Grace是一个开源、轻巧、易用同时特性丰富、性能优秀的依赖注入容器框架。从这篇IOC容器评测文章找到的Grace,评测显示这款开源轻巧的框架性能挺成熟优秀的,但是中文资料几乎找不到,作者文档也不多,Get Started在各种项目中的案例也没有。这里贴一下介绍和纯后台以及ASP.NET Core的使用Demo,部分翻译自项目,更多内容可以直接看项目的Readme和Tests——Tests作者分类很好,可以作为需求切入点了解。

    作者:Ian Johnson

    主项目Nuget:https://www.nuget.org/packages/Grace/

    主项目Github:https://github.com/ipjohnson/Grace

     ASP.Net Core Nuget: https://www.nuget.org/packages/Grace.AspNetCore.MVC

     ASP.Net Core Github:https://github.com/ipjohnson/Grace.DependencyInjection.Extensions

    目录:

    一、介绍

    二、纯C#使用Grace的Demo

    三、ASP.NET Core使用Grace的Demo

    四、ASP.NET MVC使用Grace的Demo

    五、多个构造方法

    结尾

    一、介绍

    Grace有如下特性:

    • 配置提供允许最大限度扩展的流式(Fluent)接口/属性
    • 支持子容器和轻量级生命周期作用域
    • 支持绑定上下文化(类似NInject)
    • 容器创建的IDisposable对象将被跟踪和释放,除非另有配置
    • 性能特点使它成为最快的容器之一
    • 支持特殊类型
      • IEnumerable<T> - 支持将集合解析为IEnumerable<T>,包括其他如List<T>,ReadOnlyCollection<T>,T[]和其他实现ICollection<T>的集合。具体可以查看这里,可以实现批量自动注册绑定。
      • Func<T> - 支持自动解析Func<T>
      • Lazy<T> - 当解析一个Lazy<T>对象时,其将在自己创建的生命周期内创建和解析对象T
      • Owned<T> - 在一个Owned<T>对象内解析时,其将有与自己Owned<T>相关联的生命周期(与Autofac类似)
      • Meta<T> - 在一个Meta<T>内解析时,其元数据也会跟着解析
      • 自定义委托 - 任何返回一个类型的委托都能被自动解析
      • 用Grace.Factory自定义接口
    • 支持多种生命周期,包括单例、作用域内单例、请求内单例(MVC4, MVC5 和 WCF 扩展包中)、对象图内单例、基类上单例和弱单例。如果以上都没有符合需求的,可以使用ICompiledLifeStyle接口实现
    • 内置支持装饰器设计
    • 支持自定义包装(Func<T>和Meta<T>是内置包装的举例)
    • ASP.Net Core支持(测试似乎只支持.Net Standard 1.0,DotNetCore 2.0Linux下不行)
    • ASP.Net MVC 4 & 5支持

    二、纯C#使用Grace的Demo

    假设我们有Repository和Service模式,有用户(User)和账户(Account)这两个DAO对象,分别如下定义好接口和类,此部分在附件的“IOCFramework.Dao”工程中。

     1 public interface IAccountRepository
     2 {
     3     string Get();
     4 }
     5 
     6 public class AccountRepository : IAccountRepository
     7 {
     8     public string Get()
     9     {
    10         return "[Account]简单注册调用[Repo]";
    11     }
    12 }
    13 
    14 public interface IAccountService
    15 {
    16     string Get();
    17 }
    18 
    19 public class AccountService : IAccountService
    20 {
    21     IAccountRepository _accountRepository;
    22     public AccountService(IAccountRepository accountRepository)
    23     {
    24         _accountRepository = accountRepository;
    25     }
    26 
    27     public string Get()
    28     {
    29         return _accountRepository.Get() + "[Service]";
    30     }
    31 }
    32 
    33 public interface IUserRepository
    34 {
    35     string Get();
    36 }
    37 
    38 public class UserRepositoryA : IUserRepository
    39 {
    40     public string Get()
    41     {
    42         return "[User]键值注册调用A[Repo]";
    43     }
    44 }
    45 
    46 public class UserRepositoryB : IUserRepository
    47 {
    48     public UserRepositoryB(string param1, string param2)
    49     {
    50         Console.WriteLine($"Ctor param1:{param1}");
    51     }
    52 
    53     public string Get()
    54     {
    55         return "[User]键值注册调用B[Repo]";
    56     }
    57 }
    58 
    59 public interface IUserService
    60 {
    61     string Get();
    62 }
    63 
    64 public class UserService : IUserService
    65 {
    66 
    67     IUserRepository _userRepository;
    68 
    69     public UserService(IUserRepository userRepository)
    70     {
    71         _userRepository = userRepository;
    72     }
    73 
    74     public string Get()
    75     {
    76         return _userRepository.Get() + "[Service]";
    77     }
    78 }
    View Code

    接下来可以使用源码也可以添加Nuget的方式添加Grace。下面的代码实现了纯C#的容器的注册和实现,过程简单,看注释即可,不在赘述,此部分在附件的“IOCFramework.Demo”工程中。

     1 public static void Exec()
     2 {
     3     //容器
     4     var container = new DependencyInjectionContainer();
     5 
     6     container.Configure(m =>
     7     {
     8         //这里演示如何简单注册同一个接口对应其实现
     9         m.Export<AccountRepository>().As<IAccountRepository>();
    10         m.Export<AccountService>().As<IAccountService>();
    11 
    12         //这里演示如何使用键值注册同一个接口的多个实现
    13         m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
    14         //这里同时演示使用带参数构造器来键值注册
    15         m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; });
    16 
    17         //这里演示依赖倒置而使用构造器带键值注入
    18         m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
    19     });
    20 
    21     //获取简单注册实例
    22     var accountRepo = container.Locate<IAccountRepository>();
    23     Console.WriteLine(accountRepo.Get());//输出:[Account]简单注册调用[Repo]
    24     var accountSvc = container.Locate<IAccountService>();
    25     Console.WriteLine(accountSvc.Get());//输出:[Account]简单注册调用[Repo][Service]
    26 
    27     Console.WriteLine();
    28 
    29     //获取指定键值的实例
    30     var userRepo = container.Locate<IUserRepository>(withKey: "A");
    31     Console.WriteLine(userRepo.Get());//输出:[User]键值注册调用A[Repo]
    32 
    33     var userSvc = container.Locate<IUserService>();//输出:Ctor param1:kkkkk
    34     Console.WriteLine(userSvc.Get());//输出:[User]键值注册调用B[Repo][Service]
    35 }

    三、ASP.NET Core使用Grace的Demo

    此部分Demo在附件的“WebCoreApplicationUseGrace”工程中。

    1. 先在项目中添加Grace.AspNetCore.Hosting包。依赖项有Grace包和Grace.DependencyInjection.Extensions包,也可以使用这里的源码:https://github.com/ipjohnson/Grace.DependencyInjection.Extensions

    2. 然后在Program.cs添加Grace组件:

    1 public static IWebHost BuildWebHost(string[] args) =>
    2     WebHost.CreateDefaultBuilder(args)
    3         .UseKestrel()
    4         .UseIISIntegration()
    5         .UseContentRoot(Directory.GetCurrentDirectory())
    6         .UseGrace() //添加Grace
    7         .UseStartup<Startup>()
    8         .Build();

    3. 在Startup.cs中添加方法ConfigureContainer方法,然后在IInjectionScope里面注册接口和类型,具体看下面代码。这里可以将Startup.cs修改为部分类而单独将ConfigureContainer方法放于新建的Startup.cs部分类中,方便以后添加配置

     1 public partial class Startup
     2 {
     3     // 添加此方法
     4     public void ConfigureContainer(IInjectionScope scope)
     5     {
     6         scope.Configure(m =>
     7         {
     8             //这里演示如何简单注册同一个接口对应其实现
     9             m.Export<AccountRepository>().As<IAccountRepository>();
    10             m.Export<AccountService>().As<IAccountService>();
    11 
    12             //这里演示如何使用键值注册同一个接口的多个实现
    13             m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
    14             //这里同时演示使用带参数构造器来键值注册
    15             m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; });
    16 
    17             //这里演示依赖倒置而使用构造器带键值注入
    18             m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
    19         });
    20 
    21         scope.SetupMvc();//这一句需先添加Grace.AspNetCore.MVC
    22     }
    23 }

    4. Grace提供了自定义控制器和视图激活器,用一些自定义特性提供了更好的性能。这里添加Grace.AspNetCore.MVC Nuget包/源码项目,然后在上一步方法中添加scope.SetupMvc(),至此配置完毕。

    5. 在控制器中使用方法如下。一般的可以使用构造器参数方式赋值实例,有特殊注入的(如下面带键值的注入)可以先实例IExportLocatorScope出来,再用其Locate来获得实例。

     1 public class HomeController : Controller
     2 {
     3     //IExportLocatorScope可以获取从InjectionScope和LifeTimeScope注入的类型实例
     4     IExportLocatorScope locator;
     5     
     6     IAccountRepository accountRepo;
     7     IAccountService accountSvc;
     8     IUserRepository userRepo;
     9     IUserService userSvc;
    10 
    11     public HomeController(IExportLocatorScope locator, IAccountRepository accountRepo, IAccountService accountSvc, IUserService userSvc)
    12     {
    13         this.locator = locator;
    14         //获取简单注册实例
    15         this.accountRepo = accountRepo;
    16         this.accountSvc = accountSvc;
    17         //获取指定键值的实例
    18         this.userRepo = this.locator.Locate<IUserRepository>(withKey: "A");
    19         this.userSvc = userSvc;
    20 
    21     }
    22 
    23     public IActionResult Index()
    24     {
    25         return Json(new
    26         {
    27             accountRepoGet = accountRepo.Get(),
    28             accountSvcGet = accountSvc.Get(),
    29             userRepoGet = userRepo.Get(),
    30             userSvcGet = userSvc.Get(),
    31         });
    32     }
    33     
    34 }

    以上代码输出如下:

    {
        "accountRepoGet": "[Account]简单注册调用[Repo]",
        "accountSvcGet": "[Account]简单注册调用[Repo][Service]",
        "userRepoGet": "[User]键值注册调用A[Repo]",
        "userSvcGet": "[User]键值注册调用B,Ctor param1:kkkkk[Repo][Service]"
    }

    四、ASP.NET MVC使用Grace的Demo

    Grace支持ASP.NET MVC4和MVC5,这里以MVC5为例,此部分Demo在附件的“WebCoreApplicationUseGrace”工程中。

    1. 先在项目中添加Grace.MVC5包,依赖项有Grace包。也可以使用这里的源码:https://github.com/ipjohnson/Grace.MVC

    2. 新建一个类,用于配置注册接口及其实现

     1 public class DependencyInjectionScope
     2 {
     3     public static DependencyInjectionContainer GetContainer()
     4     {
     5         //容器
     6         var container = new DependencyInjectionContainer();
     7         container.Configure(m =>
     8         {
     9             //这里演示如何简单注册同一个接口对应其实现
    10             m.Export<AccountRepository>().As<IAccountRepository>();
    11             m.Export<AccountService>().As<IAccountService>();
    12 
    13             //这里演示如何使用键值注册同一个接口的多个实现
    14             m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
    15             //这里同时演示使用带参数构造器来键值注册
    16             m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; });
    17 
    18             //这里演示依赖倒置而使用构造器带键值注入
    19             m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
    20         });
    21 
    22         return container;
    23     }
    24 }

    3. 在Global.asax.cs中MvcApplication类的Application_Start方法指定MVC的控制器实例工厂为Grace的DisposalScopeControllerActivator,至此配置完毕。

     1 public class MvcApplication : System.Web.HttpApplication
     2 {
     3     protected void Application_Start()
     4     {
     5         AreaRegistration.RegisterAllAreas();
     6         FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
     7         RouteConfig.RegisterRoutes(RouteTable.Routes);
     8         BundleConfig.RegisterBundles(BundleTable.Bundles);
     9 
    10         //获取注册容器
    11         var graceIocContainer = DependencyInjectionScope.GetContainer();
    12         //MVC指定Grace的工厂为控制器实例工厂
    13         ControllerBuilder.Current.SetControllerFactory(new DisposalScopeControllerActivator(graceIocContainer));
    14     }
    15 }

    4. 在控制器中使用方法如下。一般的可以使用构造器参数方式赋值实例,有特殊注入的(如下面带键值的注入)可以先实例IExportLocatorScope出来,再用其Locate来获得实例。

     1 public class HomeController : Controller
     2 {
     3     //IExportLocatorScope可以获取从InjectionScope和LifeTimeScope注入的类型实例
     4     IExportLocatorScope locator;
     5 
     6     IAccountRepository accountRepo;
     7     IAccountService accountSvc;
     8     IUserRepository userRepo;
     9     IUserService userSvc;
    10 
    11     public HomeController(IExportLocatorScope locator, IAccountRepository accountRepo, IAccountService accountSvc, IUserService userSvc)
    12     {
    13         this.locator = locator;
    14         //获取简单注册实例
    15         this.accountRepo = accountRepo;
    16         this.accountSvc = accountSvc;
    17         //获取指定键值的实例
    18         this.userRepo = this.locator.Locate<IUserRepository>(withKey: "A");
    19         this.userSvc = userSvc;
    20 
    21     }
    22 
    23     public ActionResult Index()
    24     {
    25         return Json(new
    26         {
    27             accountRepoGet = accountRepo.Get(),
    28             accountSvcGet = accountSvc.Get(),
    29             userRepoGet = userRepo.Get(),
    30             userSvcGet = userSvc.Get(),
    31         }, JsonRequestBehavior.AllowGet);
    32     }
    35 }

    以上代码输出如下:

    {
        "accountRepoGet": "[Account]简单注册调用[Repo]",
        "accountSvcGet": "[Account]简单注册调用[Repo][Service]",
        "userRepoGet": "[User]键值注册调用A[Repo]",
        "userSvcGet": "[User]键值注册调用B,Ctor param1:kkkkk[Repo][Service]"
    }

    五、多个构造方法

    Grace支持多个构造函数,当然是单一构造函数注入的,有相关Tests可以参考:https://github.com/ipjohnson/Grace/blob/master/tests/Grace.Tests/Classes/Simple/MultipleConstructorImport.cs

    在以下代码中有两个构造方法,Grace选了最多参数的构造方法进行Import,这里构造方法的继承语句(: this(userSvc))是无关的,用不用都可以。

     1 public class MultipleConstructorImportService : IMultipleConstructorImportService
     2 {
     3     IUserService userSvc;
     4     IAccountService accountSvc;
     5     public MultipleConstructorImportService(IUserService userSvc)
     6     {
     7         this.userSvc = userSvc;
     8 
     9         Console.WriteLine($"构造函数1:userSvc:{this.userSvc != null}, accountSvc: {this.accountSvc != null}");
    10         //输出:构造函数1:userSvc:True, accountSvc: False
    11     }
    12 
    13 
    14     public MultipleConstructorImportService(IAccountService accountSvc, IUserService userSvc)
    15         : this(userSvc)
    16     {
    17         //this.userSvc = userSvc;
    18         this.accountSvc = accountSvc;
    19 
    20         Console.WriteLine($"构造函数2:userSvc:{this.userSvc != null}, accountSvc: {this.accountSvc != null}");
    21         //输出:构造函数2:userSvc:True, accountSvc: True
    22     }
    23 
    24 }

    结尾:

    Grace的其他特性用法可以参看项目的Tests例子和项目里面的Wiki,后面有时间再慢慢贴上来咯。这里给大家附上文章的源码Demo或者查看GitHub项目

  • 相关阅读:
    第二次冲刺每日站立会议10(完结)
    第二次冲刺每日站立会议09
    第二次冲刺每日站立会议08
    找bug
    测试计划
    博客园的意见与建议
    第二次每日站立会议07
    个人总结
    学习进度条(第十六周)
    梦断代码阅读笔记03
  • 原文地址:https://www.cnblogs.com/huangsheng/p/introduce-ioc-container-grace.html
Copyright © 2020-2023  润新知