Внедрение зависимостей с классами, которые наследуются от одного и того же интерфейса. Консоль .Net Core - PullRequest
0 голосов
/ 17 февраля 2019

Я работаю над консольным приложением .Net с двумя классами (GenerateCsv, GenerateTxt), которые наследуются от одного и того же интерфейса (GenerateFile).Оба они принимают интерфейсы IReadFile, IWriteFile в качестве параметров конструктора.

Я хочу использовать Dependency Injection, чтобы создать правильный класс для генерации файла и вернуть строковое значение по окончании.

static void Main()
    {
        string result="Processed Nothing";

        Console.WriteLine("To Generate: \n");
        Console.WriteLine("1) English CSV File Type 1 \n"+
                          "2) Englsih Txt File Type 2 \n" +
                          "3) To Exit Type 3 \n");

        var userInput = Convert.ToInt32(Console.ReadLine());

        if (userInput == 10)
            Environment.Exit(0);

        var service = BuildDependencies.Build();

        Console.WriteLine("Started Generating");
        Console.WriteLine("Working, please wait");

        switch (userInput)
        {
            case 1:
                result = service.GetService<IGenerateFile>().GenerateFile();
                break;
            case 2:
                result = service.GetService<IGenerateFile>().GenerateFile();
                break;
        }
        Console.WriteLine(result);

class BuildDependencies

using Microsoft.Extensions.DependencyInjection;
public static class BuildDependencies
{
    public static ServiceProvider Build()
    {
        var services = new ServiceCollection()
            .AddTransient<IReadFile, ReaFile>()
            .AddTransient<IWriteFile, WriteFile>()
            .AddTransient<IGenerateFile,GenerateCsv>()
            .AddTransient<IGenerateFile,GenerateTxt>()
            .BuildServiceProvider();

        return services;
    }

Ответы [ 2 ]

0 голосов
/ 18 февраля 2019

Вы должны заменить блок switch(userinput) вызовом фабричного класса.

Контракт этого класса может выглядеть примерно так:

interface IFileGeneratorFactory
{
    // I used int because it's in your example code, but an enum would be a stronger contract
    IGenerateFile GetFileGenerator(int userInput);
}

, а реализация выглядит так:

class FileGeneratorFactory : IFileGeneratorFactory
{
    readonly IServiceProvider serviceProvider;

    public FileGeneratorFactory(IServiceProvider serviceProvider)
        => this.serviceProvider = serviceProvider;

    public IGenerateFile GetFileGenerator(int userInput)
    {
        switch (userInput)
        {
            // The Factory has explicit knowledge of the different generator types,
            // but returns them using a common interface. The consumer remains
            // unconcerned with which exact implementation it's using.
            case 1:
                return serviceProvider.GetService<GenerateCsv>();
            case 2:
                return serviceProvider.GetService<GenerateTxt>();
            default:
                throw new InvalidOperationException($"No generator available for user input {userInput}");
        }
    }
}

Зарегистрируйте классы как:

var services = new ServiceCollection()
    .AddTransient<GenerateCsv>()
    .AddTransient<GenerateTxt>()
    .AddTransient<IFileGeneratorFactory, FileGeneratorFactory>();

Затем используйте как:

IGenerateFile fileGenerator = service
    .GetService<IFileGeneratorFactory>()
    .GetFileGenerator(userInput);

string result = fileGenerator.GenerateFile();

Эта реализация фабрики довольно проста.Я написал его таким образом, чтобы он был доступным, но я настоятельно рекомендую взглянуть на более элегантные примеры в документах Simple Injector .Поскольку эти примеры нацелены на Simple Injector, а не на ASP.Net DI, для их работы может потребоваться некоторая настройка, но базовые шаблоны должны работать для любой инфраструктуры DI.

0 голосов
/ 17 февраля 2019

Вы можете создать два интерфейса

interface IGenerateCsv : IGenerateFile{}
interface IGenerateTxt: IGenerateFile{}

, а затем зарегистрировать их у своего провайдера DI:

public static ServiceProvider Build()
{
    var services = new ServiceCollection()
        .AddTransient<IReadFile, ReaFile>()
        .AddTransient<IWriteFile, WriteFile>()
        .AddTransient<IGenerateCsv ,GenerateCsv>()
        .AddTransient<IGenerateTxt,GenerateTxt>()
        .BuildServiceProvider();

    return services;
}

Затем вы можете создать другой класс, который имеет оба экземпляра и решает, какой из них вызвать.Затем внедрите этот класс в своего потребителя.

enum OutputType
{
    Csv,
    Text
}

interface IFileGenerator
{
    void GenerateFile(OutputType outputType);
}

class FileGenerator : IFileGenerator
{
    // inject both or your file services into the constructor


    public void GenerateFile(OutputType outputType)
    {
         switch(outputType)
         {
              // call the correct service here
         }
    }

Затем зарегистрируйте IFileGenerator

public static ServiceProvider Build()
{
    var services = new ServiceCollection()
    .AddTransient<IReadFile, ReaFile>()
    .AddTransient<IWriteFile, WriteFile>()
    .AddTransient<IGenerateCsv ,GenerateCsv>()
    .AddTransient<IGenerateTxt, GenerateTxt>()
    .AddTransient<IFileGenerator, FileGenerator>()
    .BuildServiceProvider();

    return services;
}

И сделайте что-то подобное в своем классе потребителя:

class Consumer
{
    private readonly IFileGenerator fileGenerator;

    Consumer(IFileGenerator, fileGenerator)
    {
        this.fileGenerator = fileGenerator;
    }

    public void SomeMethod(string userInput)
    {
          switch(userInput)
          {
                case 1: 
                    fileGenerator.GenerateFile(OutputType.Csv);
                    break;

                case 2: 
                    fileGenerator.GenerateFile(OutputType.Text);
                    break;

                default:
                    break;
          }
    }
}

То естьпри условии, что у вас есть какой-то потребительский класс.Если вы просто работаете в методе Main, сделайте что-то вроде этого:

var serviceProvider = BuildDependencies.Build();
var fileGenerator = serviceProvider.GetService<IFileGenerator>();
...