.NET 5完全自配置服务

目录

介绍

背景

使用代码

兴趣点


介绍

在ASP.Core应用程序(不限于ASP.Core)中,当我们想使用DI容器中的构建时,我们需要创建服务,然后在Startup.cs的ConfigureServices方法中注册它们。我想简化它并在完全自注册服务中采用旧的M.E.F方法。

方法很简单:

  1. 使用[Service]或[SingeltonService]特性注释您的服务
  2. 调用 ServiceLocator.LoadServices
  3. 获益?

背景

这个想法来自现在已经过时的方法扩展框架(M.E.F.),它是.Nets (First?) DI容器的方法,这种基于注解的服务发现很流行。

使用代码

首先让我们来看看这两个特性:

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class SingeltonServiceAttribute : ServiceAttribute
{
    public SingeltonServiceAttribute(params Type[] registerAs) : base(registerAs)
    {
        
    }
}
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = true)]
public class ServiceAttribute : Attribute
{
    public ServiceAttribute(params Type[] registerAs)
    {
        RegisterAs = registerAs;
    }

    public IEnumerable<Type> RegisterAs { get; set; }
}

它们都是代表我们的服务标记的属性,但也包括一个Type列表来表达声明类型应该注册的所有类型。现在让我们注释一个服务:

[SingeltonService(typeof(IFooRepository), typeof(IDatabaseRepository))]
public class FooRepository : IFooRepository
{
    public string Connection { get; set; }
    public async Task DoStuff() { }
}

public interface IFooRepository : IDatabaseRepository
{
    Task DoStuff();
}

public interface IDatabaseRepository
{
    string Connection { get; set; }
}

这将我们标记FooRepository为单例服务,但也请求IFooRepository并且IDatabaseRepository也应该指向同一个实例。因此,如果我们请求FooRepository或IFooRepository或 IDatabaseRepository,我们总是得到相同的实例(因为单例服务)。

由于我们现在已经注册了服务,让我们在我们的ServiceLocator帮助下将其加载到DI容器中:

public static class ServiceLocator
{
    public static void LoadServices(IServiceCollection services)
    {
        foreach (var type in AppDomain.CurrentDomain.GetAssemblies().SelectMany
                (f => f.GetTypes())
            .Where(e => e.GetCustomAttribute<ServiceAttribute>(false) != null))
        {
            var serviceAttribute = type.GetCustomAttribute<ServiceAttribute>(false);
            var actorTypes = serviceAttribute.RegisterAs;

            if (serviceAttribute is SingeltonServiceAttribute)
            {
                services.AddSingleton(type);
                foreach (var actorType in actorTypes)
                {
                    services.AddSingleton(actorType, (sCol) => sCol.GetService(type));
                }
            }
            else
            {
                services.AddScoped(type);
                foreach (var actorType in actorTypes)
                {
                    services.AddScoped(actorType, (sCol) => sCol.GetService(type));
                }
            }
        }
    }
}
public class MyStartup
{
    public MyStartup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    
    // This method gets called by the runtime. 
    // Use this method to add services to the container.
    // For more information on how to configure your application, 
    // visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        ServiceLocator.LoadServices(services);
    }
}

就是这样。调用ServiceLocator.LoadServices启动Service发现并加载所有服务。

兴趣点

在更大的项目中,该ConfigurateServices方法可以增长很多,如果应该配置自身的服务(DI规则)也可以注入自身,我发现它非常有用。

这不是配置服务的任何其他方式所独有的,应该与手动注册一起运行良好。

https://www.codeproject.com/Tips/5311615/Completely-Selfconfigurating-Service-with-NET-5

上一篇:KVSSD: 结合 LSM 与 FTL 以实现写入优化的 KV 存储


下一篇:IntelliJ IDEA 中一个服务按多个端口同时启动与显示Services面板