【问题标题】:Applying Dependency Injection to different implementations of an abstract class c#将依赖注入应用于抽象类 c# 的不同实现
【发布时间】:2019-09-22 19:24:28
【问题描述】:

当我可以在我的控制器中注入抽象类的 2 个不同实现时,我想在我的 .net 核心应用程序中构建一个 MVC 场景。这些实现调用它们的外部相关 API。也许架构是错误的,因此我请教您的建议,但请先按照我的想法跟随我。 我创建了一个通用抽象类。为什么要抽象?因为调用 API 的基本方式/属性对每个人都是一样的。就我而言,到目前为止,我只有一个 HttpClient。

public abstract class ApiCaller
{
    protected static HttpClient client;
    protected static string ApiUrl;

    public ApiCaller(string apiUrl)
    {
        client = new HttpClient();
        ApiUrl = apiUrl;
    }

    public abstract string GetApiResultAsync();
}

之后,我将拥有两个不同的类 Api1ServiceApi2Service,它们扩展了 ApiCaller,并且将有自己不同的方式来调用它们的相关 API .

public class Api1Service : ApiCaller
{
    public Api1Service(string apiUrl) : base(apiUrl)
    {

    }

    public override string GetApiResultAsync()
    {
        ...
    }
}


public class Api2Service : ApiCaller
{
    public Api2Service(string apiUrl) : base(apiUrl)
    {

    }

    public override string GetApiResultAsync()
    {
        ...
    }
}

现在,在我的控制器中,我想同时注入两个实例,因为我想同时使用这两个业务服务。但我不知道这是否可行。

[Route("api/[controller]")]
[ApiController]
public class MyController : ControllerBase
{
    private readonly ApiCaller _apiCaller;

    public BooksAndAlbumsController(ApiCaller apiCaller)
    {
        _apiCaller = apiCaller;
    }

    [HttpPost]
    public void Post([FromBody] string value)
    {
        _apiCaller.GetApiResultAsync() //but I want to use both my apiCallers
    }
}

因此,不知何故,我需要在我的容器中注册我的抽象类的两个实现。我怎样才能做到这一点?如果您发现我的架构中存在缺陷,请告诉我!

【问题讨论】:

    标签: c# api asp.net-core model-view-controller dependency-injection


    【解决方案1】:

    您可以注入IEnumerable<ApiCaller>,然后同时使用它们。

    在容器中注册两个 ApiCaller,然后在控制器中注入 IEnumerable<ApiCaller>

    类似这样的:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ApiCaller, Api1Service>();
        services.AddSingleton<ApiCaller, Api2Service>();
    }
    

    我的控制器

    [Route("api/[controller]")]
    [ApiController]
    public class MyController : ControllerBase
    {
        private readonly IEnumerable<ApiCaller> _apiCallers;
    
        public MyController(IEnumerable<ApiCaller> apiCallers)
        {
            _apiCallers = apiCallers;
        }
    
        [HttpPost]
        public async Task Post([FromBody] string value)
        {
            // Loop through one by one or call them in parallel, up to you.
            foreach(var apiCaller in _apiCallers)
            {
                var result = await apiCaller.GetApiResultAsync();
            }
        }
    }
    

    另一种可能性是注册 Api1Service 和 Api2Service 然后像这样注入它们。但它不会像第一个解决方案那样动态/灵活。

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<Api1Service>();
        services.AddSingleton<Api2Service>();
    }
    

    我的控制器

    [Route("api/[controller]")]
    [ApiController]
    public class MyController : ControllerBase
    {
        private readonly Api1Service _api1Service;
        private readonly Api2Service _api2Service;
    
        public MyController(Api1Service api1Service, Api2Service api2Service)
        {
            _api1Service = api1Service;
            _api2Service = api2Service;
        }
    
        [HttpPost]
        public async Task Post([FromBody] string value)
        {
            var result1 = await apiService1.GetApiResultAsync();
            var result2 = await apiService2.GetApiResultAsync();
        }
    }
    

    【讨论】:

      【解决方案2】:

      检查Composite Pattern

      public sealed class CompositeApiCaller : ApiCaller
      {
          private const string SEPARATION_STRING = Environnement.NewLine;
      
          private ApiCaller[] _apiCallers;
      
          public CompositeApiCaller(params ApiCaller[] apiCallers)
          {
              _apiCallers = apiCallers;
          }
      
          public override string GetApiResultAsync()
          {
              var builder = new StringBuilder();
      
              for (int i = 0; i < _apiCallers.Length; i++)
              {
                  if (i > 0)
                      builder.Append(SEPARATION_STRING);
      
                  builder.Append(apiCaller.GetApiResultAsync());
              }
      
              return builder.ToString();
          }
      }
      

      【讨论】:

        【解决方案3】:

        你可以使用 NamedHttpClients 和一个工厂

        public static class NamedHttpClients {
          public const string StarTrekApi = "StarTrekApi";
          public const string StarWarsApi = "StarWarsApi";
        }
        
        
        services.AddHttpClient(NamedHttpClients.StarTrekApi, client => {
            client.BaseAddress = new Uri("http://stapi.co/api/v1/rest");
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("apiClientTest", "1.0"));
        });
        
        services.AddHttpClient(NamedHttpClients.StarWarsApi, client => {
          client.BaseAddress = new Uri("https://swapi.co/api/");
          client.DefaultRequestHeaders.Accept.Clear();
          client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
          client.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("apiClientTest", "1.0"));
        });
        

        然后创建一个工厂,该工厂将被注入到控制器中

        public interface IFanApiClientFactory {
          IFanApiClient CreateStarWarsApiClient();
          IFanApiClient CreateStarTrekApiClient();
        }
        
        public class FanApiClientFactory : IFanApiClientFactory {
          private readonly IHttpClientFactory _httpClientFactory;
        
          public FanApiClientFactory(IHttpClientFactory httpClientFactory) {
            _httpClientFactory = httpClientFactory;
          }
        
          public IFanApiClient CreateStarWarsApiClient() {
            var client = _httpClientFactory.CreateClient(NamedHttpClients.StarWarsApi);
            return new StarWarsApiClient(client);
          }
        
          public IFanApiClient CreateStarTrekApiClient() {
            var client = _httpClientFactory.CreateClient(NamedHttpClients.StarTrekApi);
            return new StarTrekApiClient(client);
          }
        }
        

        注册工厂

        services.AddSingleton<IFanApiClientFactory, FanApiClientFactory>();
        

        至少实现具体的 api 客户端

        public class StarWarsApiClient : IFanApiClient {
          private readonly HttpClient _client;
        
          public StarWarsApiClient(HttpClient client) {
            _client = client;
          }
        
          public async Task<string> GetMostImportantPerson() {
            var response = await _client.GetAsync("people/1");
            return await response.Content.ReadAsStringAsync();
          }
        }
        
        
        public class StarTrekApiClient : IFanApiClient {
        
          private readonly HttpClient _client;
        
          public StarTrekApiClient(HttpClient client) {
            _client = client;
          }
        
          public async Task<string> GetMostImportantPerson() {
            var response = await _client.GetAsync("character/CHMA0000126904");
            return await response.Content.ReadAsStringAsync();
          }
        }
        

        最后是控制器

        public class HomeController : Controller {
        
          private readonly IFanApiClientFactory _fanApiClientFactory;
        
          public HomeController(IFanApiClientFactory fanApiClientFactory) {
            _fanApiClientFactory = fanApiClientFactory;
          }
        
          public async Task<IActionResult> Index() {
        
            var starWarsApiClient = _fanApiClientFactory.CreateStarWarsApiClient();
            var starTrekApiClient = _fanApiClientFactory.CreateStarTrekApiClient();
        
            var person1 = await starTrekApiClient.GetMostImportantPerson();
            var person2 = await starWarsApiClient.GetMostImportantPerson();
        
            return View();
          }
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-09-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-09-13
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多