Dependency injection asp.net核心服务定位器在cosole应用中如何避免

Dependency injection asp.net核心服务定位器在cosole应用中如何避免,dependency-injection,console-application,asp.net-core,Dependency Injection,Console Application,Asp.net Core,在使用控制台应用程序时,我对如何避免使用服务定位器有点困惑 节目 public static int Main(string[] args) { // Configuration var configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").AddEnvironmentVariables().Build(); // DI container

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

节目

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>();
    }
我需要将IExampleRepository注入到新的Task1()中

任务1

public class Task1
{
    public Task1()
    {

    }

    private readonly IExampleRepository _exampleRepository;

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


    public void Process() {
      ....
    }
所以基本上我的理解是我注册了依赖项,然后我应该能够在我的类中注入它们。我不确定是否需要将我的服务提供商传下去


我相信在MVC中,有一种神奇的力量恰好可以实现这一点。在不使用服务定位器模式的情况下如何进行注入?

基本上,您不需要将
IServiceProvider
传递给除引导程序(
Startup
)或工厂方法/类以外的任何类,因为这会将您的类绑定到特定的IoC容器

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

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

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

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

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

这是我尝试在测试应用程序中使用您的代码,但我不确定是否正确

我还不确定如何在MyTaskApplication CreateNewTask(connectionString)中为方法传递连接字符串

它是否需要作为属性、MyTaskApplication构造函数的一部分或替代方法传入

public class Program
{
    public static void Main(string[] args)
    {
        var services = new ServiceCollection();
        services.AddScoped<Task1>();
        services.AddScoped<MyTaskApplication>();
        services.AddTransient<ITaskFactory, TaskFactory>();
        var serviceProvider = services.BuildServiceProvider();

        var m = serviceProvider.GetService<MyTaskApplication>();
        m.Run();

    }
}

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

public interface ITaskFactory
{
    Task1 CreateNewTask();

    Task1 CreateNewTask(string connectionString);
}

public class MyTaskApplication
{
    private readonly ITaskFactory taskFactory;
    private string tenantConnectionString;

    public MyTaskApplication(ITaskFactory taskFactory)
    {
        this.taskFactory = taskFactory;
    }

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

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

        Console.WriteLine("Running");
    }
}

public class Task1
{
    private string _repositoryText;

    public Task1()
    {
        _repositoryText = String.Empty;   
    }

    public Task1(string repositoryText)
    {
        _repositoryText = repositoryText;
    }

    public void Process()
    {
        Console.WriteLine("process: " + _repositoryText);
    }
}
公共类程序
{
公共静态void Main(字符串[]args)
{
var services=newservicecolection();
services.addScope();
services.addScope();
services.AddTransient();
var serviceProvider=services.BuildServiceProvider();
var m=serviceProvider.GetService();
m、 Run();
}
}
公共类TaskFactory:ITaskFactory
{
私人只读服务器ViceProvider服务;
公共任务工厂(IServiceProvider服务)
{
这是服务=服务;
}
公共任务1 CreateNewTask()
{            
//获取默认任务服务,该服务与以前一样是暂时的
//因此,每次调用都会得到一个新实例
return services.GetService();
}
公共任务1 CreateNewTask(字符串连接字符串)
{
//例如,当有多个租户时,您希望
//数据库上的任务,该任务仅在
//编译时不知道runtime.connectionString,因为
//用户可以选择要处理哪一个
//var dbContext=MyDbContext(connectionString);
//var repository=newexamplepository(dbContext);
返回新Task1(connectionString);
}
}
公共接口ITaskFactory
{
Task1 CreateNewTask();
Task1 CreateNewTask(字符串连接字符串);
}
公共类MyTaskApplication
{
私有只读ITaskFactory taskFactory;
私有字符串租户连接字符串;
公共MyTaskApplication(ITaskFactory taskFactory)
{
this.taskFactory=taskFactory;
}
公开募捐
{
//appsettings.json中带有默认连接字符串的默认实例
var task1=taskFactory.CreateNewTask();
task1.Process();
//作为字符串传入的租户配置
var task2=taskFactory.CreateNewTask(租户连接字符串);
task2.Process();
控制台。写入线(“运行”);
}
}
公共课任务1
{
私有字符串_repositoryText;
公共任务1()
{
_repositoryText=String.Empty;
}
公共任务1(字符串存储文本)
{
_repositoryText=repositoryText;
}
公共程序()
{
Console.WriteLine(“进程:”+\u repositoryText);
}
}

谢谢您的详细解释,如果要了解工厂实施的一个小例子,会不会太麻烦?我倾向于学习更好的例子。我在看这个:分别为factory方法和factory类添加了两个示例感谢您的努力:)事实上,我正在努力解决如何调用MyTaskApplication。。。我不确定我是否正确地解决了我的DI,看你的第二个例子,它期待一个ITaskFactory,但我认为DI应该能够解决它?你也应该向DI注册你的
MyTaskApplication
<代码>服务。AddSingleton()IServiceProvider(通过
IServiceProvider=services.BuildServiceProvider();
之后,您可以使用
var application=provider.GetService()
解析主应用程序,最后使用
application.run()运行它
。有关更完整的示例,请参阅。我认为我需要将我的任务作为临时任务
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); 
    }
}
public class Program
{
    public static void Main(string[] args)
    {
        var services = new ServiceCollection();
        services.AddScoped<Task1>();
        services.AddScoped<MyTaskApplication>();
        services.AddTransient<ITaskFactory, TaskFactory>();
        var serviceProvider = services.BuildServiceProvider();

        var m = serviceProvider.GetService<MyTaskApplication>();
        m.Run();

    }
}

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

public interface ITaskFactory
{
    Task1 CreateNewTask();

    Task1 CreateNewTask(string connectionString);
}

public class MyTaskApplication
{
    private readonly ITaskFactory taskFactory;
    private string tenantConnectionString;

    public MyTaskApplication(ITaskFactory taskFactory)
    {
        this.taskFactory = taskFactory;
    }

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

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

        Console.WriteLine("Running");
    }
}

public class Task1
{
    private string _repositoryText;

    public Task1()
    {
        _repositoryText = String.Empty;   
    }

    public Task1(string repositoryText)
    {
        _repositoryText = repositoryText;
    }

    public void Process()
    {
        Console.WriteLine("process: " + _repositoryText);
    }
}