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

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”工程中。

 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介绍的更多相关文章

  1. Ninject是一款.Net平台下的开源依赖注入框架

    Ninject是一款.Net平台下的开源依赖注入框架.按照官方说法,它快如闪电.超级轻量,且充分利用了.Net的最新语法,使用Lambda表达式代替Xml文件完成类型绑定.Ninject结构精巧,功能 ...

  2. Spring.NET依赖注入框架学习--简单对象注入

    Spring.NET依赖注入框架学习--简单对象注入 在前面的俩篇中讲解了依赖注入的概念以及Spring.NET框架的核心模块介绍,今天就要看看怎么来使用Spring.NET实现一个简单的对象注入 常 ...

  3. [ASP.NET Core 3框架揭秘] 依赖注入[4]:一个Mini版的依赖注入框架

    在前面的章节中,我们从纯理论的角度对依赖注入进行了深入论述,我们接下来会对.NET Core依赖注入框架进行单独介绍.为了让读者朋友能够更好地理解.NET Core依赖注入框架的设计与实现,我们按照类 ...

  4. ASP.NET MVC IOC依赖注入之Autofac系列(一)- MVC当中应用

    话不多说,直入主题看我们的解决方案结构: 分别对上面的工程进行简单的说明: 1.TianYa.DotNetShare.Model:为demo的实体层 2.TianYa.DotNetShare.Repo ...

  5. Android Dagger依赖注入框架浅析

    今天接触了Dagger这套android的依赖注入框架(DI框架).感觉跟Spring 的IOC差点儿相同吧.这个框架它的优点是它没有採用反射技术(Spring是用反射的),而是用预编译技术.因为基于 ...

  6. 依赖注入及AOP简述(四)——“好莱坞原则”和依赖注入框架简介 .

    3.2.    “好莱坞原则” 看了前面关于依赖注入概念的描述,我们来提炼出依赖注入的核心思想.如果说传统的组件间耦合方式,例如new.工厂模式等,是一种由开发者主动去构建依赖对象的话,那么依赖注入模 ...

  7. Spring 之 控制反转(IoC), 依赖注入(DI)和面向切面(AOP)

    关于依赖注入, 这篇博文写的非常简单易懂. https://github.com/android-cn/blog/tree/master/java/dependency-injection 此外, 博 ...

  8. .net core程序中使用微软的依赖注入框架

    我之前在博文中介绍过Asp.net core下系统自带的依赖注入框架,这个依赖框架在Microsoft.Extensions.DependencyInjection中实现,本身并不是.net core ...

  9. Spring.NET依赖注入框架学习--简介

    Spring.NET依赖注入框架学习--Spring.NET简介 概述 Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序.它提供了很多方面的功能,比如依赖注入. ...

随机推荐

  1. tomcat 启动中文乱码

        1.情景展示 从Apache官网下载的tomcat,启动后中文日志信息显示乱码. 启动startup.bat后 2.原因分析 通过修改日志输出的字符集来解决. 3.解决方案 tomcat安装目 ...

  2. docker安装 与 基本配置

    1.安装docker #yum remove docker \ docker-common \ container-selinux \ docker-selinux \ docker-engine \ ...

  3. 二叉树 B-树B+树

    聚集索引和非聚集索引结构参考:http://blog.csdn.net/cangchen/article/details/44818623 前两天有位朋友邀请我回答个问题,为什么 MongoDB (索 ...

  4. Parallel.For循环与普通的for循环

    前两天看书发现了一个新的循环Parallel.For,这个循环在循环期间可以创建多个线程并行循环,就是说循环的内容是无序的.这让我想到了我前面的牛牛模拟计算是可以用到这个循环的,我前面的牛牛模拟计算是 ...

  5. rabbitmq在linux下单节点部署和基本使用

    RabbitMQ是基于erlang开发的消息服务,官网为:https://www.rabbitmq.com,RabbitMQ要依赖erlang运行,所以要先安装erlang环境,rabbitmq可以用 ...

  6. 三句话看明白jdk收费吗

    对于从oracle下载的jdk8:JDK8u200(含)以下版本不收费. 对于从oracle下载的jdk11:JDK 11.0.0不收费,JDK 11.0.1不收费. 对于openjdk:免费 ——— ...

  7. CMake版本低,需要更高版本.

    https://blog.csdn.net/qq_34935373/article/details/90266958 使用cmake命令安装Opencv软件时,报如下错误: CMake Error a ...

  8. Consider defining a bean of type 'com.*.*.feign.*FeignClient' in your configuration.

    Description: Field *FeignClient in com.*.*.service.* required a bean of type '***********' that coul ...

  9. 自定义MyBatis

    自定义MyBatis是为了深入了解MyBatis的原理 主要的调用是这样的: //1.读取配置文件 InputStream in = Resources.getResourceAsStream(&qu ...

  10. Python3 打包exe

    cx_Freeze(不推荐) 以前只用 cx_Freeze 支持将 python3 打包成 exe ,示例如下: 在你要打包的 python 文件下新建这个 setup.py 文件: #!/usr/b ...