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
目錄:
一、介紹
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 }
接下來可以使用源碼也可以添加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項目

浙公網安備 33010602011771號