我们在使用AddScoped、AddTransient、AddSingleton这类方法的时候很是麻烦。我们每增加一个接口以及其实现的时候,是不是需要在这里硬编码注册一行代码呢?项目小还好,但当我们的项目变得庞大之后,这里的依赖注入怎么来维护呢?
在网上翻了半天,看了很多方法,其实现代码感觉都很不优雅,想想还是自己写一个比较实用吧,我们只需按照一个规定来定义和实现接口。应用程序就能自动扫描并注册这些程序集中的接口和对应实现类,完成依赖注入的自动注册,具体的实现可以通过接口或特性来实现,具体实现如下:
第一步:创建如下这样空接口,用于标记需要注入
public interface IDependency
{
}
/// <summary>
/// 实现该接口将自动注册到Ioc容器,生命周期为每次请求创建一个实例
/// </summary>
public interface IScopeDependency: IDependency
{
}
/// <summary>
/// 实现该接口将自动注册到Ioc容器,生命周期为单例
/// </summary>
public interface ISingletonDependency : IDependency
{
}
/// <summary>
/// 实现该接口将自动注册到Ioc容器,生命周期为每次创建一个新实例
/// </summary>
public interface ITransientDependency : IDependency
{
}
第二步:创建需要注入的功能接口,这类接口就是你想实现某些功能的封装,它们都继承第一步创建的标记接口,如下,我创建一个功能接口
public interface IUserService: IScopeDependency
{
}
第三步:实现需要注入的功能接口,如下,我创建了一个实现UserService的类
public class UserService: IUserService
{
}
第四步:扩展IServiceCollection批量注入方法AddDataService
/// <summary>
/// 要扫描的程序集名称
/// 默认为[^Shop.Utils|^Shop.]多个使用|分隔
/// </summary>
public static string MatchAssemblies = "^Shop.Utils|^Shop.";
public static IServiceCollection AddDataService(this IServiceCollection services)
{
#region 依赖注入
//services.AddScoped<IUserService, UserService>();
var baseType = typeof(IDependency);
var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;
var getFiles = Directory.GetFiles(path, "*.dll").Where(Match); //.Where(o=>o.Match())
var referencedAssemblies = getFiles.Select(Assembly.LoadFrom).ToList(); //.Select(o=> Assembly.LoadFrom(o))
var ss = referencedAssemblies.SelectMany(o => o.GetTypes());
var types = referencedAssemblies
.SelectMany(a => a.DefinedTypes)
.Select(type => type.AsType())
.Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();
var implementTypes = types.Where(x => x.IsClass).ToList();
var interfaceTypes = types.Where(x => x.IsInterface).ToList();
foreach (var implementType in implementTypes)
{
if (typeof(IScopeDependency).IsAssignableFrom(implementType))
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
services.AddScoped(interfaceType, implementType);
}
else if(typeof(ISingletonDependency).IsAssignableFrom(implementType))
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
services.AddSingleton(interfaceType, implementType);
}
else
{
var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));
if (interfaceType != null)
services.AddTransient(interfaceType, implementType);
}
}
#endregion
return services;
}
/// <summary>
/// 程序集是否匹配
/// </summary>
public static bool Match(string assemblyName)
{
assemblyName = Path.GetFileName(assemblyName);
if (assemblyName.StartsWith($"{AppDomain.CurrentDomain.FriendlyName}.Views"))
return false;
if (assemblyName.StartsWith($"{AppDomain.CurrentDomain.FriendlyName}.PrecompiledViews"))
return false;
return Regex.IsMatch(assemblyName, MatchAssemblies, RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
第四步:在Startup.cs调用AddDataService方法进行批量注入
public void ConfigureServices(IServiceCollection services)
{
services.AddDataService();
}
测试