C# 使用继承自同一接口控制台.Net内核的类进行依赖注入

C# 使用继承自同一接口控制台.Net内核的类进行依赖注入,c#,dependency-injection,.net-core,console-application,C#,Dependency Injection,.net Core,Console Application,我正在开发一个.Net核心控制台应用程序,它有两个类(GenerateCsv、GenerateText),它们继承自同一个接口(GenerateFile)。它们都将IReadFile、IWriteFile接口作为构造函数参数 我想使用依赖项注入创建正确的类来生成文件,并在完成时返回字符串值 static void Main() { string result="Processed Nothing"; Console.WriteLine("To Gener

我正在开发一个.Net核心控制台应用程序,它有两个类(GenerateCsv、GenerateText),它们继承自同一个接口(GenerateFile)。它们都将IReadFile、IWriteFile接口作为构造函数参数

我想使用依赖项注入创建正确的类来生成文件,并在完成时返回字符串值

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);
static void Main()
{
string result=“未处理”;
Console.WriteLine(“生成:\n”);
Console.WriteLine(“1)英文CSV文件类型1\n”+
“2)英语Txt文件类型2\n”+
“3)退出类型3\n”);
var userInput=Convert.ToInt32(Console.ReadLine());
如果(userInput==10)
环境。退出(0);
var service=BuildDependencies.Build();
Console.WriteLine(“已开始生成”);
Console.WriteLine(“正在工作,请稍候”);
开关(用户输入)
{
案例1:
结果=service.GetService().GenerateFile();
打破
案例2:
结果=service.GetService().GenerateFile();
打破
}
控制台写入线(结果);
类构建依赖项

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;
    }
使用Microsoft.Extensions.DependencyInjection;
公共静态类BuildDependencies
{
公共静态ServiceProvider生成()
{
var services=newservicecolection()
.AddTransient()
.AddTransient()
.AddTransient()
.AddTransient()
.BuildServiceProvider();
返回服务;
}

您可以创建两个接口

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;
}
然后注册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;
}
这是假设您有某种消费类。如果您只是在使用Main方法,请执行以下操作:

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

您应该将
开关(userinput)
块替换为对factory类的调用

该类的契约可能类似于:

接口IFileGeneratorFactory
{
//我使用int是因为它在您的示例代码中,但enum将是一个更强大的契约
IGenerateFile GetFileGenerator(int userInput);
}
实施过程如下:

类FileGeneratorFactory:IFileGeneratorFactory
{
只读IServiceProvider服务提供商;
公共文件生成器工厂(IServiceProvider服务提供商)
=>this.serviceProvider=serviceProvider;
公共IGenerateFile GetFileGenerator(int userInput)
{
开关(用户输入)
{
//工厂对不同的发电机类型有明确的了解,
//但是使用公共接口返回它们。使用者仍然
//不关心它使用的具体实现。
案例1:
返回serviceProvider.GetService();
案例2:
返回serviceProvider.GetService();
违约:
抛出新的InvalidOperationException($“没有可用于用户输入的生成器{userInput}”);
}
}
}
按如下方式注册课程:

var services=newservicecolection()
.AddTransient()
.AddTransient()
.AddTransient();
然后,像这样消费:

IGeneratFile文件生成器=服务
.GetService()
.GetFileGenerator(用户输入);
字符串结果=fileGenerator.GenerateFile();

这个工厂实现非常简单。我这样写是为了保持它的可接近性,但我强烈建议查看中更优雅的示例。因为这些示例针对的是简单注入器,而不是ASP.Net DI,它们可能需要一些调整才能为您工作——但基本模式应该适用于任何DI框架。

d谢谢,但我如何才能将FileGenerator类注入到我的使用者中?当我执行代码时,它会转到FileGeneratorFactory执行内部代码,但FileGenerator始终为空。您可能需要稍微调整服务注册--可能需要根据具体类型注册
GenerateCsv
GenerateXT
而不是它们的接口。如果你找到了,请告诉我,我会更新示例以使其更加完整。如果你仍然卡住,我今天晚些时候会再次检查我的代码,看看缺少什么。谢谢,这很有效。但这是正确的方法吗?你的意思是,注册具体类型正确吗?在这种情况下,我会说它是正确的。DI容器在解析类型之前需要被告知类型。您可以添加唯一的接口(
IGenerateCV
/
IGeneratext
)并使用这些接口进行注册/解析,但我认为当您仅通过工厂解析它们时,这不会给您带来任何好处。我已经更新了我的答案,以包含一个注册示例。
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;
          }
    }
}
var serviceProvider = BuildDependencies.Build();
var fileGenerator = serviceProvider.GetService<IFileGenerator>();