目录
介绍
在ASP.Core应用程序(不限于ASP.Core)中,当我们想使用DI容器中的构建时,我们需要创建服务,然后在Startup.cs的ConfigureServices方法中注册它们。我想简化它并在完全自注册服务中采用旧的M.E.F方法。
方法很简单:
- 使用[Service]或[SingeltonService]特性注释您的服务
- 调用 ServiceLocator.LoadServices
- 获益?
背景
这个想法来自现在已经过时的方法扩展框架(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