KeyedServiceについて語る

.NET8では、KeyedServiceが導入され、カスタムキーワードを使用してサービスを利用できるようになりました。これは、複数のインターフェースサブクラスを注入する際に、命名キーワードで区別し、使用時にキーワードを使用して呼び出すことを意味します。
次に例を見てみましょう。通知サービスインターフェースINotifyServiceには、SMSServiceとEMailServiceの二つのサブクラスがあり、これらのサブクラスは異なるRespositoryを呼び出します。

```C#
public interface INotifyService{    
  bool Notify(string message);
}

public class SMSService : INotifyService{    
  private readonly Dictionary<string, dynamic> _configs;    
  private readonly ILogger<EMailService> _logger;    
  public SMSService([FromKeyedServices("smsRep")] IConfigRepository configRepository, ILogger<EMailService> logger)    {        
    _logger = logger;        
    _configs = configRepository.GetConfig();    
  }    
  public bool Notify(string message)    {        
    _logger.LogInformation($"{_configs["name"]},SMSService,こちらで設定ファイルに基づいてSMS通知の送信が完了します,Message:{message}");        
    return true;    
  }
}

public class EMailService : INotifyService{    
  private readonly Dictionary<string, dynamic> _configs;    
  private readonly ILogger<EMailService> _logger;    
  public EMailService([FromKeyedServices("emailRep")] IConfigRepository configRepository, ILogger<EMailService> logger)    {        
    _logger = logger;        
    _configs = configRepository.GetConfig();    
  }    
  public bool Notify(string message)    {        
    _logger.LogInformation($"{_configs["name"]},EMailService,こちらで設定ファイルに基づいてメール通知の送信が完了します,Message:{message}");        
    return true;    
  }
}

public interface IConfigRepository{    
  Dictionary<string, dynamic> GetConfig();
}

public class IEMailConfigRepository : IConfigRepository{    
  public Dictionary<string, dynamic> GetConfig()    {        
    // データベースから設定情報を取得する        
    return new Dictionary<string, dynamic>() { { "name", "メール設定" } };    
  }
}

public class ISMSConfigRepository : IConfigRepository{    
  public Dictionary<string, dynamic> GetConfig()    {        
    // データベースから設定情報を取得する        
    return new Dictionary<string, dynamic>() { { "name", "SMS設定" } }; ;    
  }
}
```

最後に、KeyedServiceを使用する場合、特性[FromKeyedServices("命名")]が必要ですが、この特性はapp.Mapメソッドで直接使用することはできません。そこで、SMSとEMailという二つのクラスを定義し、AddScoped方式での注入を行います。

```C#
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using System.Collections;
using System.Collections.Generic;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Services.AddScoped<SMS>();
builder.Services.AddScoped<EMail>();

builder.Services.AddKeyedScoped<IConfigRepository, ISMSConfigRepository>("smsRep");
builder.Services.AddKeyedScoped<IConfigRepository, IEMailConfigRepository>("emailRep");

builder.Services.AddKeyedScoped<INotifyService, EMailService>("emailSev");
builder.Services.AddKeyedScoped<INotifyService, SMSService>("smsSev");
var app = builder.Build();

app.MapGet("/sms", ([FromServices]SMS sms, string message) => new { Result = sms.Notify(message), Messgae = message, Type = sms.GetType().Name });
app.MapGet("/email", ([FromServices]EMail email, string message) => new { Result = email.Notify(message), Messgae = message, Type = email.GetType().Name });

//誤った書き方
//app.MapGet("/sms", ([FromKeyedServices("smsSev")] INotifyService notifyService, string message) => new { Result = notifyService.Notify(message), Messgae = message, Type = notifyService.GetType().Name });
//app.MapGet("/email", ([FromKeyedServices("smsSev")] INotifyService notifyService, string message) => new { Result = notifyService.Notify(message), Messgae = message, Type = notifyService.GetType().Name });

app.Run();

public class SMS([FromKeyedServices("smsSev")] INotifyService notifyService){
  public bool Notify(string message)    {        
    return notifyService.Notify(message);    
  }
}

public class EMail([FromKeyedServices("emailSev")] INotifyService notifyService){
  public bool Notify(string message)    {        
    return notifyService.Notify(message);    
  }
}

// もう一つの書き方は、AddSingleton方式で注入し、app.Services.GetRequiredKeyedService方式で通知インタフェースを取得し、使用することです。
builder.Services.AddKeyedSingleton<IConfigRepository, IEMailConfigRepository>("emailRep");
builder.Services.AddKeyedSingleton<INotifyService, EMailService>("emailSev");
var app = builder.Build();
app.MapGet("/email", (string message) =>{
  var email = app.Services.GetRequiredKeyedService<INotifyService>("emailSev");
  return new { Result = email.Notify(message), Messgae = message, Type = email.GetType().Name };
});
```
私が使用した感想としては、KeyedServiceの使用に多くの制限があり、スムーズではないと感じます。これはPreview段階であるためかもしれませんが、正式版での改善を期待しています。
(Translated by GPT)

元のリンク：https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247487066&idx=1&sn=7e7222da26ede37c0c3b503a0736c773&chksm=9f005170a877d866bee7149c755b92a177953c178c05c2cf2c9bad401fc8443d0bd08cfe5d5d&token=2050451386&lang=zh_CN#rd