c# - asp.net 核心服务定位器如何在控制台应用程序中避免

标签 c# asp.net-core dependency-injection console-application

我对如何在使用控制台应用程序时避免服务定位器感到有点困惑

程序

public static int Main(string[] args)
{        
    // Configuration
        var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").AddEnvironmentVariables().Build();

        // DI container
        var services = new ServiceCollection();
        ConfigureServices(services, configuration);
        var serviceProvider = services.BuildServiceProvider();

        // Do I pass along the serviceProvider?
        // Can resolve using locator pattern do I just use this in my classes?
        // var exampleRepository = _serviceProvider.GetService<IExampleRepository>();

          // Execute the correct command based on args
        return CommandLineOptions.Execute(args);

}

 private static void ConfigureServices(IServiceCollection services, IConfiguration configuration)
    {
        services.AddScoped<ApplicationDbContext>((s) => new ApplicationDbContext(configuration.GetSection("Data:DefaultConnection:ConnectionString").Value));
        services.AddScoped<IExampleRepository, ExampleRepository>();
    }

命令行选项

public static class CommandLineOptions
{        
    public static int Execute(string[] args, IServiceProvider serviceProvider)
    {
        try
        {
            var app = new CommandLineApplication
            {
                Name = "dnx abc",
                FullName = "Abc Commands",
                Description = "ABC",

            };

            app.VersionOption("--version", PlatformServices.Default.Application.ApplicationVersion);
            app.HelpOption("-?|-h|--help");

            app.OnExecute(() =>
                {
                    //ShowLogo();
                    app.ShowHelp();
                    return 2;
                });

            app.Command(
            "task",
            task=>
            {
                task.Name = "Task1";
                task.FullName = "Task1";
                task.Description = "Tasks";                    
                task.HelpOption("-?|-h|--help");
                task.OnExecute(() => { task.ShowHelp(); return 0; });

                task.Command(
                    "task1",
                    data =>
                    {
                        data.FullName = "Task1 command";
                        data.Description = "Task1";

                        data.OnExecute(() =>
                        {
                            // Need to inject 
                            var p = new Task1();
                            p.Process()  

                            return 0;
                        });

我需要将 IExampleRepository 注入(inject)新的 Task1()

任务 1

public class Task1
{
    public Task1()
    {

    }

    private readonly IExampleRepository _exampleRepository;

    public Task1(IExampleRepository exampleRepository)
    {
        _exampleRepository = exampleRepository;
    }


    public void Process() {
      ....
    }

所以基本上我的理解是我注册了我的依赖项,然后我应该能够在我的类(class)中注入(inject)它们。我不确定是否需要将我的 serviceProvider 传递下去?

我相信在 MVC 中有魔法恰好可以实现这一点。如果不使用服务定位器模式,我将如何进行注入(inject)?

最佳答案

基本上,您不希望必须将 IServiceProvider 传递给除 Bootstrap (Startup) 或工厂方法/类之外的任何类,因为这将您的类与特定的IoC 容器。

您可以做的是将依赖项添加到您的 CommandLineApplication 类并在 Main 方法中解析它,然后您可以从这里开始您的依赖项注入(inject)链。只要您需要/想要一次解决所有依赖关系,这就会起作用。

当你遇到只需要加载它的一个子集的情况(即在传递某个参数时使用不同的服务或程序逻辑),你将需要一种工厂(工厂是一个瘦在传递对象之前创建和配置对象的包装器,在 IoC 的情况下,它还解决了依赖关系)。

在工厂实现中,如有必要,可以引用容器(您需要范围内的依赖项或每个对象创建的 transient 解析)。如果您需要多个 Task1 实例,您还需要一个工厂。

有两种方法。对于非常简单的工厂,您可以使用工厂方法,它可以在您进行 IServiceCollection 注册时直接使用。

services.AddTransient<Task1>();
services.AddTransient<Func<Task1>>( (serviceProvider) => {
    return () => serviceProvider.GetService<Task1>();
});

然后注入(inject)你的依赖。

public class MyTaskApplication
{
    private readonly Func<Task> taskFactory;
    public MyApplicationService(Func<Task> taskFactory)
    {
         this.taskFactory = taskFactory;
    }

    public void Run() 
    {
        var task1 = taskFactory(); // one instance
        var task2 = taskFactory(); // another instance, because its registered as Transient
    }
}

如果您需要更复杂的配置或带有运行时参数,那么制作一个工厂类可能更有意义。

public class TaskFactory : ITaskFactory
{
    private readonly IServiceProvider services;

    public TaskFactory(IServiceProvider services)
    {
         this.services = services;
    }

    public Task1 CreateNewTask() 
    {
        // get default task service, which is transient as before
        // so you get a new instance per call
        return services.GetService<Task1>();
    }

    public Task1 CreateNewTask(string connectionString)
    {
         // i.e. when having multiple tenants and you want to 
         // to the task on a database which is only determined at 
         // runtime. connectionString is not know at compile time because 
         // the user may choose which one he wants to process

         var dbContext = MyDbContext(connectionString);
         var repository = new ExampleRepository(dbContext);

         return new Task1(repository);
    }
}

以及用法

public class MyTaskApplication
{
    private readonly ITaskFactory taskFactory;
    public MyApplicationService(ITaskFactory taskFactory)
    {
         this.taskFactory = taskFactory;
    }

    public void Run() 
    {
        // Default instance with default connectionString from appsettings.json
        var task1 = taskFactory.CreateNewTask();

        // Tenant configuration you pass in as string
        var task2 = taskFactory.CreateNewTask(tenantConnectionString); 
    }
}

关于c# - asp.net 核心服务定位器如何在控制台应用程序中避免,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36927869/

相关文章:

c# - 将 IQueryable 类型转换为 Linq to Entities 中的接口(interface)

c# - 如何为 "Start"和 "Update"等常用函数添加自动完成

c# - Asp.net core 2.0 POST 端点在没有 json 有效负载的情况下工作,但在使用 json 有效负载时失败(400 错误请求)

c# - 如何在 .NET Core ConsoleApp 中实例化 ServiceCollection 并使用 IHttpClientFactory?

android - Koin 安卓测试

c# - Unity 在向 OWIN 添加 Active Directory 访问权限时尝试实例化枚举参数

c# - appveyor 中的 xunit.console 在寻找 xunit.dll 时失败

c# - Entity Framework 正在替换我的对象的 ID

visual-studio - VS2019中ASP.NET Core 2.2项目中的分析器警告

visual-studio-2015 - ASP.net 5 Beta 8 无法在 Visual Studio 2015 中安装