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项目

posted @ 2018-01-13 15:32  朝野布告  阅读(1759)  评论(0编辑  收藏  举报