纯手撸.NETCore服务自动注册-接口方式

2,125 阅读2分钟

!!! 本文已参与「新人创作礼」活动,一起开启掘金创作之路。更多干货文章,可以访问 菜鸟厚非

介绍

用过 ASP.NET Core 的朋友都知道,默认的 IoC 容器它提供了基本的 AddXXXX 方法来绑定实例关系,但是对于大型项目来说,还是挺困难的,大型的项目需要的是通用的注册,不可能手动添加每个对象的解析关系,这才是我们面临的痛点。

解决方案

  1. 可以继续使用默认的 IoC 容器通过程序初始化加载进行注册,还可以使用第三方 Castle Windsor 替换 ASP.NET Core 默认容器,本篇讲解使用 默认 IoC 容器方式。

默认 IoC 容器

  1. 定义三种生命周期不同的接口 ITransientDependency、ISingletonDependency、IScopedDependency ,具体不做解锁根据英文单词也应该知道
public interface ITransientDependency
{

}

public interface ISingletonDependency
{

}

public interface IScopedDependency
{

}
  1. ServiceCollection 扩展方法
public static class ServiceCollectionExtension
{
    public static void ServiceRegister(this IServiceCollection services, params Assembly[] assemblies)
    {
        if (assemblies.Count() <= 0)
        {
            return;
        }
        foreach (var assembly in assemblies)
        {
            
            var allTypes = assembly.GetTypes();
            foreach (var type in allTypes)
            {
                if (typeof(ITransientDependency).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                {
                    //  获取当前实现类的接口,但不包含我们的标记类
                    var interfaceTypes = type.GetInterfaces().Where(p => !p.FullName.Contains("ITransientDependency"));
                    foreach (var interfaceType in interfaceTypes)
                    {
                        services.AddTransient(interfaceType,type);
                    }
                }
            }

            foreach (var type in allTypes)
            {
                if (typeof(ISingletonDependency).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                {
                    //  获取当前实现类的接口,但不包含我们的标记类
                    var interfaceTypes = type.GetInterfaces().Where(p => !p.FullName.Contains("ISingletonDependency"));
                    foreach (var interfaceType in interfaceTypes)
                    {
                        services.AddSingleton(interfaceType, type);
                    }
                }
            }

            foreach (var type in allTypes)
            {
                if (typeof(IScopedDependency).IsAssignableFrom(type) && type.IsClass && !type.IsAbstract)
                {
                    //  获取当前实现类的接口,但不包含我们的标记类
                    var interfaceTypes = type.GetInterfaces().Where(p => !p.FullName.Contains("IScopedDependency"));
                    foreach (var interfaceType in interfaceTypes)
                    {
                        services.AddScoped(interfaceType, type);
                    }
                }
            }
        }

    }
}
  1. 注册服务,在正式应用场景中,我们是非常可能需要把多个程序集都按约定来注册的,个人建议的做法是,在每个需要注册的程序集中添加一个标志该程序集的基类,比如在 Service 这个程序集中添加一个 IServiceBase 接口,这样就可以通过typeof(IServiceBase).Assembly 获取到这个程序集进而用来注册
public void ConfigureServices(IServiceCollection services)
{
    services.ServiceRegister(Assembly.GetExecutingAssembly());
}
  1. 按约定进行继承
public interface IPostBlogService : ITransientDependency
{  
     Result Post(BlogDto dto);
}

public class PostBlogService : IPostBlogService
{
    public Result Post(BlogDto dto) 
    { 
       //todo: post this blog  
    }
}
  1. 总结 三个生命周期标记接口,只是都属于一种约定,基于约定进行注册,只是容器不一样而已。你可能有疑惑,你每次都扫描进程集效率会不会很低,其实 ConfigureServices 只是在你应用启动时才运行一次,也就是说我们只扫描一次进程集,不存在性能问题。