zoukankan      html  css  js  c++  java
  • 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项目

  • 相关阅读:
    Codeforces Round #649 (Div. 2) D. Ehab's Last Corollary
    Educational Codeforces Round 89 (Rated for Div. 2) E. Two Arrays
    Educational Codeforces Round 89 (Rated for Div. 2) D. Two Divisors
    Codeforces Round #647 (Div. 2) E. Johnny and Grandmaster
    Codeforces Round #647 (Div. 2) F. Johnny and Megan's Necklace
    Codeforces Round #648 (Div. 2) G. Secure Password
    Codeforces Round #646 (Div. 2) F. Rotating Substrings
    C++STL常见用法
    各类学习慕课(不定期更新
    高阶等差数列
  • 原文地址:https://www.cnblogs.com/huangsheng/p/introduce-ioc-container-grace.html
Copyright © 2011-2022 走看看