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”工程中。
public interface IAccountRepository
{
string Get();
} public class AccountRepository : IAccountRepository
{
public string Get()
{
return "[Account]简单注册调用[Repo]";
}
} public interface IAccountService
{
string Get();
} public class AccountService : IAccountService
{
IAccountRepository _accountRepository;
public AccountService(IAccountRepository accountRepository)
{
_accountRepository = accountRepository;
} public string Get()
{
return _accountRepository.Get() + "[Service]";
}
} public interface IUserRepository
{
string Get();
} public class UserRepositoryA : IUserRepository
{
public string Get()
{
return "[User]键值注册调用A[Repo]";
}
} public class UserRepositoryB : IUserRepository
{
public UserRepositoryB(string param1, string param2)
{
Console.WriteLine($"Ctor param1:{param1}");
} public string Get()
{
return "[User]键值注册调用B[Repo]";
}
} public interface IUserService
{
string Get();
} public class UserService : IUserService
{ IUserRepository _userRepository; public UserService(IUserRepository userRepository)
{
_userRepository = userRepository;
} public string Get()
{
return _userRepository.Get() + "[Service]";
}
}
接下来可以使用源码也可以添加Nuget的方式添加Grace。下面的代码实现了纯C#的容器的注册和实现,过程简单,看注释即可,不在赘述,此部分在附件的“IOCFramework.Demo”工程中。
public static void Exec()
{
//容器
var container = new DependencyInjectionContainer(); container.Configure(m =>
{
//这里演示如何简单注册同一个接口对应其实现
m.Export<AccountRepository>().As<IAccountRepository>();
m.Export<AccountService>().As<IAccountService>(); //这里演示如何使用键值注册同一个接口的多个实现
m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
//这里同时演示使用带参数构造器来键值注册
m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; }); //这里演示依赖倒置而使用构造器带键值注入
m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
}); //获取简单注册实例
var accountRepo = container.Locate<IAccountRepository>();
Console.WriteLine(accountRepo.Get());//输出:[Account]简单注册调用[Repo]
var accountSvc = container.Locate<IAccountService>();
Console.WriteLine(accountSvc.Get());//输出:[Account]简单注册调用[Repo][Service] Console.WriteLine(); //获取指定键值的实例
var userRepo = container.Locate<IUserRepository>(withKey: "A");
Console.WriteLine(userRepo.Get());//输出:[User]键值注册调用A[Repo] var userSvc = container.Locate<IUserService>();//输出:Ctor param1:kkkkk
Console.WriteLine(userSvc.Get());//输出:[User]键值注册调用B[Repo][Service]
}
三、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组件:
public static IWebHost BuildWebHost(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseKestrel()
.UseIISIntegration()
.UseContentRoot(Directory.GetCurrentDirectory())
.UseGrace() //添加Grace
.UseStartup<Startup>()
.Build();
3. 在Startup.cs中添加方法ConfigureContainer方法,然后在IInjectionScope里面注册接口和类型,具体看下面代码。这里可以将Startup.cs修改为部分类而单独将ConfigureContainer方法放于新建的Startup.cs部分类中,方便以后添加配置
public partial class Startup
{
// 添加此方法
public void ConfigureContainer(IInjectionScope scope)
{
scope.Configure(m =>
{
//这里演示如何简单注册同一个接口对应其实现
m.Export<AccountRepository>().As<IAccountRepository>();
m.Export<AccountService>().As<IAccountService>(); //这里演示如何使用键值注册同一个接口的多个实现
m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
//这里同时演示使用带参数构造器来键值注册
m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; }); //这里演示依赖倒置而使用构造器带键值注入
m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
}); scope.SetupMvc();//这一句需先添加Grace.AspNetCore.MVC
}
}
4. Grace提供了自定义控制器和视图激活器,用一些自定义特性提供了更好的性能。这里添加Grace.AspNetCore.MVC Nuget包/源码项目,然后在上一步方法中添加scope.SetupMvc(),至此配置完毕。
5. 在控制器中使用方法如下。一般的可以使用构造器参数方式赋值实例,有特殊注入的(如下面带键值的注入)可以先实例IExportLocatorScope出来,再用其Locate来获得实例。
public class HomeController : Controller
{
//IExportLocatorScope可以获取从InjectionScope和LifeTimeScope注入的类型实例
IExportLocatorScope locator; IAccountRepository accountRepo;
IAccountService accountSvc;
IUserRepository userRepo;
IUserService userSvc; public HomeController(IExportLocatorScope locator, IAccountRepository accountRepo, IAccountService accountSvc, IUserService userSvc)
{
this.locator = locator;
//获取简单注册实例
this.accountRepo = accountRepo;
this.accountSvc = accountSvc;
//获取指定键值的实例
this.userRepo = this.locator.Locate<IUserRepository>(withKey: "A");
this.userSvc = userSvc; } public IActionResult Index()
{
return Json(new
{
accountRepoGet = accountRepo.Get(),
accountSvcGet = accountSvc.Get(),
userRepoGet = userRepo.Get(),
userSvcGet = userSvc.Get(),
});
} }
以上代码输出如下:
{
"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. 新建一个类,用于配置注册接口及其实现
public class DependencyInjectionScope
{
public static DependencyInjectionContainer GetContainer()
{
//容器
var container = new DependencyInjectionContainer();
container.Configure(m =>
{
//这里演示如何简单注册同一个接口对应其实现
m.Export<AccountRepository>().As<IAccountRepository>();
m.Export<AccountService>().As<IAccountService>(); //这里演示如何使用键值注册同一个接口的多个实现
m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A");
//这里同时演示使用带参数构造器来键值注册
m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(() => { return "kkkkk"; }); //这里演示依赖倒置而使用构造器带键值注入
m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");
}); return container;
}
}
3. 在Global.asax.cs中MvcApplication类的Application_Start方法指定MVC的控制器实例工厂为Grace的DisposalScopeControllerActivator,至此配置完毕。
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles); //获取注册容器
var graceIocContainer = DependencyInjectionScope.GetContainer();
//MVC指定Grace的工厂为控制器实例工厂
ControllerBuilder.Current.SetControllerFactory(new DisposalScopeControllerActivator(graceIocContainer));
}
}
4. 在控制器中使用方法如下。一般的可以使用构造器参数方式赋值实例,有特殊注入的(如下面带键值的注入)可以先实例IExportLocatorScope出来,再用其Locate来获得实例。
public class HomeController : Controller
{
//IExportLocatorScope可以获取从InjectionScope和LifeTimeScope注入的类型实例
IExportLocatorScope locator; IAccountRepository accountRepo;
IAccountService accountSvc;
IUserRepository userRepo;
IUserService userSvc; public HomeController(IExportLocatorScope locator, IAccountRepository accountRepo, IAccountService accountSvc, IUserService userSvc)
{
this.locator = locator;
//获取简单注册实例
this.accountRepo = accountRepo;
this.accountSvc = accountSvc;
//获取指定键值的实例
this.userRepo = this.locator.Locate<IUserRepository>(withKey: "A");
this.userSvc = userSvc; } public ActionResult Index()
{
return Json(new
{
accountRepoGet = accountRepo.Get(),
accountSvcGet = accountSvc.Get(),
userRepoGet = userRepo.Get(),
userSvcGet = userSvc.Get(),
}, JsonRequestBehavior.AllowGet);
}
}
以上代码输出如下:
{
"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))是无关的,用不用都可以。
public class MultipleConstructorImportService : IMultipleConstructorImportService
{
IUserService userSvc;
IAccountService accountSvc;
public MultipleConstructorImportService(IUserService userSvc)
{
this.userSvc = userSvc; Console.WriteLine($"构造函数1:userSvc:{this.userSvc != null}, accountSvc: {this.accountSvc != null}");
//输出:构造函数1:userSvc:True, accountSvc: False
} public MultipleConstructorImportService(IAccountService accountSvc, IUserService userSvc)
: this(userSvc)
{
//this.userSvc = userSvc;
this.accountSvc = accountSvc; Console.WriteLine($"构造函数2:userSvc:{this.userSvc != null}, accountSvc: {this.accountSvc != null}");
//输出:构造函数2:userSvc:True, accountSvc: True
} }
结尾:
Grace的其他特性用法可以参看项目的Tests例子和项目里面的Wiki,后面有时间再慢慢贴上来咯。这里给大家附上文章的源码Demo或者查看GitHub项目
C#下IOC/依赖注入框架Grace介绍的更多相关文章
- Ninject是一款.Net平台下的开源依赖注入框架
Ninject是一款.Net平台下的开源依赖注入框架.按照官方说法,它快如闪电.超级轻量,且充分利用了.Net的最新语法,使用Lambda表达式代替Xml文件完成类型绑定.Ninject结构精巧,功能 ...
- Spring.NET依赖注入框架学习--简单对象注入
Spring.NET依赖注入框架学习--简单对象注入 在前面的俩篇中讲解了依赖注入的概念以及Spring.NET框架的核心模块介绍,今天就要看看怎么来使用Spring.NET实现一个简单的对象注入 常 ...
- [ASP.NET Core 3框架揭秘] 依赖注入[4]:一个Mini版的依赖注入框架
在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...
- ASP.NET MVC IOC依赖注入之Autofac系列(一)- MVC当中应用
话不多说,直入主题看我们的解决方案结构: 分别对上面的工程进行简单的说明: 1.TianYa.DotNetShare.Model:为demo的实体层 2.TianYa.DotNetShare.Repo ...
- Android Dagger依赖注入框架浅析
今天接触了Dagger这套android的依赖注入框架(DI框架).感觉跟Spring 的IOC差点儿相同吧.这个框架它的优点是它没有採用反射技术(Spring是用反射的),而是用预编译技术.因为基于 ...
- 依赖注入及AOP简述(四)——“好莱坞原则”和依赖注入框架简介 .
3.2. “好莱坞原则” 看了前面关于依赖注入概念的描述,我们来提炼出依赖注入的核心思想.如果说传统的组件间耦合方式,例如new.工厂模式等,是一种由开发者主动去构建依赖对象的话,那么依赖注入模 ...
- Spring 之 控制反转(IoC), 依赖注入(DI)和面向切面(AOP)
关于依赖注入, 这篇博文写的非常简单易懂. https://github.com/android-cn/blog/tree/master/java/dependency-injection 此外, 博 ...
- .net core程序中使用微软的依赖注入框架
我之前在博文中介绍过Asp.net core下系统自带的依赖注入框架,这个依赖框架在Microsoft.Extensions.DependencyInjection中实现,本身并不是.net core ...
- Spring.NET依赖注入框架学习--简介
Spring.NET依赖注入框架学习--Spring.NET简介 概述 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入. ...
随机推荐
- Unknown command 'run' - try 'help'
/******************************************************************************* * Unknown command ' ...
- python gis库
apt install python3 python3-gdal gdal-bin python3-pyproj proj-bin python3-shapely fiona python3-fion ...
- 使用kafka-python客户端进行kafka kerberos认证
之前说过python confluent kafka客户端做kerberos认证的过程,如果使用kafka python客户端的话同样也可以进行kerberos的认证,具体的认证机制这里不再描述,主要 ...
- docker swarm 集群搭建
创建一个集群 [vagrant@node1 ~]$ docker swarm init --advertise-addr 192.168.9.101 Swarm initialized: curren ...
- Chrome禁用software_reporter_tool
今天开机后,从几分钟到半个小时之间,感觉机器反应有些慢,发现CPU占用80-90%.查看任务管理器, 有一个 software_reporter_tool.exe 的程序占用了一半的CPU使用率. 转 ...
- SpringBoot入门-MongoDB(五)
安装MongoDB 安装MongoDB MongoDB用户.数据库相关命令操作 https://www.jianshu.com/p/237a0c5ad9fa # 创建用户以及角色 use spring ...
- CentOS7下安装Nexus私服及基础配置
环境准备 VMware上安装CentOS7 XShell/Xftp NexusOSS-3.10 jdk1.8 安装 使用root用户登录,将安装包均放置在/usr/local文件夹下 使用Xshell ...
- 人脸识别(基于ArcFace)
我们先来看看效果 上面是根据图片检测出其中的人脸.每个人脸的年龄还有性别,非常强大 第一步: 登录https://ai.arcsoft.com.cn/,注册开发者账号,身份认证,注册应用,得到APPI ...
- [PHP] 浅谈 Laravel Authentication 的 guards 与 providers
从文档的简单介绍上来讲,有一些抽象. 个人感觉,对于概念上的大多数不理解主要还是来自于 文档不是讲设计思路,而是实际操作. 查看英文文档,通常来说可以给你最准确的直觉,而本地翻译一般比较字面或者带有理 ...
- 最常见的Java面试题及答案汇总(四)
反射 57. 什么是反射? 反射主要是指程序可以访问.检测和修改它本身状态或行为的一种能力 Java反射: 在Java运行时环境中,对于任意一个类,能否知道这个类有哪些属性和方法?对于任意一个对象,能 ...