Применение Dependency Injection к различным реализациям абстрактного класса c # - PullRequest
1 голос
/ 04 мая 2019

Я хочу встроить в мое основное приложение .net сценарий MVC, когда я могу внедрить в свой контроллер 2 различные реализации абстрактного класса.Эти реализации вызывают свой внешний относительный 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();
}

После этого у меня будут два разных класса Api1Service и Api2Service , которые расширяют 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
    }
}

Итак, каким-то образом в моем контейнере мне нужно было бы зарегистрировать обе реализации моего абстрактного класса.Как мне этого добиться?Если вы видите недостатки в моей архитектуре, пожалуйста, дайте мне знать!

Ответы [ 3 ]

4 голосов
/ 04 мая 2019

Вы можете ввести IEnumerable<ApiCaller> и затем использовать их оба.

Зарегистрировать оба ApiCallers в контейнере, а затем ввести IEnumerable<ApiCaller> в свой контроллер.

Примерно так:

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

MyController

[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>();
}

MyController

[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();
    }
}
0 голосов
/ 04 мая 2019

Вы можете использовать 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();
  }
}
0 голосов
/ 04 мая 2019

Проверьте около Составной шаблон .

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();
    }
}
...