C# 依赖注入框架Grace

dahlin
6
2020-03-31

C# 依赖注入框架Grace

Grace是一个我偶尔刷技术博客的时候才知道的依赖注入框架,根据项目的github了解到,Grace是一个开源、轻巧、易用同时特性丰富、性能优秀的依赖注入容器框架。

Grace is a feature-rich dependency injection container designed with ease of use and performance in mind.

比较令人诧异的是,Grace的作者Ian Johnson,几乎没有写任何文档,而且用法也找不到任何中文资料,哪怕是英文的使用文档和教程也是很少,我试图从stack overflow上找,也是寥寥无几的问题,最后我还是从作者的Github项目主页的Issues中找到了使用的蛛丝马迹,居然是从作者的单元测试中找用法Demo。简直厉害了。

1. 项目链接

Nuget:https://www.nuget.org/packages/Grace/

Github:https://github.com/ipjohnson/Grace

测评链接 IoC Container Benchmark - Performance comparison

2. 仓储类Demo设计

按照作者给出的Demo,稍加改造一下,Ian Johnson设计了账户/用户/服务三个接口仓储类作为Demo的演示用例,我在这里列出一下。

/// <summary>
/// 账户仓储接口
/// </summary>
public interface IAccountRepository
{
	string Get();
}
/// <summary>
/// 账户仓储类
/// </summary>
public class AccountRepository : IAccountRepository
{
	public string Get()
	{
		return "[AccountRepository] 简单注册调用";
	}
}
/// <summary>
/// 账户服务接口
/// </summary>
public interface IAccountService
{
	string Get();
}
/// <summary>
/// 账户服务类
/// </summary>
public class AccountService : IAccountService
{
	IAccountRepository _accountRepository;
	public AccountService(IAccountRepository accountRepository)
	{
		_accountRepository = accountRepository;
	}

	public string Get()
	{
		return _accountRepository.Get() + " [AccountService] ";
	}
}
/// <summary>
/// 用户仓储接口
/// </summary>
public interface IUserRepository
{
	string Get();
}
/// <summary>
/// 用户仓储A类
/// </summary>
public class UserRepositoryA : IUserRepository
{
	public string Get()
	{
		return "[UserRepositoryA] 键值注册调用 ";
	}
}
/// <summary>
/// 用户仓储B类
/// </summary>
public class UserRepositoryB : IUserRepository
{
	public UserRepositoryB(string param1, string param2)
	{
		Console.WriteLine($"Ctor param1:{param1} {param2}");
	}

	public string Get()
	{
		return "[UserRepositoryB] 键值注册调用 ";
	}
}
/// <summary>
/// 用户服务接口
/// </summary>
public interface IUserService
{
	string Get();
}
/// <summary>
/// 用户服务类
/// </summary>
public class UserService : IUserService
{
	IUserRepository _userRepository;
	public UserService(IUserRepository userRepository)
	{
		_userRepository = userRepository;
	}

	public string Get()
	{
		return _userRepository.Get() + " [UserService] ";
	}
}

3. 注入实例演示

Grace框架注入操作也和其他的IoC一样,也是先声明一个容器,再按照几个特定的模式进行相应的注入,这里有点不明白的是,为何UserRepositoryB的构造参数有两个,但是注入的时候给的参数只有一个,但实例接收到的却依然是两个参数,一模一样的参数,等于复制了两份,如果有大神看懂了其中的奥妙,帮忙告诉我一下哈,十分感谢!

//容器
var container = new DependencyInjectionContainer();

container.Configure(m =>
{
	//这里演示如何简单注册同一个接口对应其实现
	m.Export<AccountRepository>().As<IAccountRepository>();
	m.Export<AccountService>().As<IAccountService>();

	//这里演示如何使用键值以单例的模式注册同一个接口的多个实现
	m.Export<UserRepositoryA>().AsKeyed<IUserRepository>("A").Lifestyle.Singleton();
	//这里同时演示使用带参数构造器来键值注册
	m.Export<UserRepositoryB>().AsKeyed<IUserRepository>("B").WithCtorParam<string>(
		() => {
			return "paramA";
			}
		);
	//这里演示依赖倒置而使用构造器带键值注入
	m.Export<UserService>().As<IUserService>().WithCtorParam<IUserRepository>().LocateWithKey("B");

});

4. 使用实例演示

使用也很简洁明了,用容器对象和注册类型定位一下就可以,关于生命周期,Grace框架里提供了几种,还可以自行扩展,这点比较强大。给定的几种模式分别是 Singleton, SingletonPerScope, SingletonPerRequest , SingletonPerObjectGraph, SingletonPerAncestor, and WeakSingleton,这几个模式的从英文意思上大概可以猜出来,但是大神 Ian Johnson 没有给出来文档和Demo,估计大神是想让我们看他的源码自己意会,也可能是大神觉得很简单,简单到不用直接说明。。。。

Many LifeStyles supported including Singleton, SingletonPerScope, SingletonPerRequest (MVC4, MVC5 & WCF packages), SingletonPerObjectGraph, SingletonPerAncestor, and WeakSingleton. If none of the provided life styles meet your need you can always implement your own ICompiledLifeStyle class.

//获取简单注册实例
var accountRepo = container.Locate<IAccountRepository>();
Console.WriteLine(accountRepo.Get());
// [AccountRepository] 简单注册调用

var accountSvc = container.Locate<IAccountService>();
Console.WriteLine(accountSvc.Get());
// [AccountRepository] 简单注册调用 [AccountService]

Console.WriteLine();

//获取指定键值的实例
var userRepo1 = container.Locate<IUserRepository>(withKey: "A");
var userRepo2 = container.Locate<IUserRepository>(withKey: "A");

// 判断单例两个对象是否相等
bool isEqual = userRepo1.Equals(userRepo2);
Console.WriteLine(isEqual); // True

Console.WriteLine(userRepo1.Get());
// [UserRepositoryA] 键值注册调用

var userRepoB = container.Locate<IUserRepository>(withKey: "B");
Console.WriteLine(userRepoB.Get());
// Ctor param: paramA paramA
// [UserRepositoryB] 键值注册调用

Console.Read();

5. 难点存疑

1、 不太清楚多个构造函数的实例怎么注入,暂时没找到Demo,可能得撸代码。
2、各种生命周期的实例还没有深入理解。

6. 相关博客链接

C#下IOC/依赖注入框架Grace介绍

动物装饰