asp.net core service locator how to avoid in cosole application
我有点困惑,在使用控制台应用程序时如何避免使用服务定位器。
程序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | 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>(); } |
命令行选项
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | 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注入新的任务1()。
任务1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | public class Task1 { public Task1() { } private readonly IExampleRepository _exampleRepository; public Task1(IExampleRepository exampleRepository) { _exampleRepository = exampleRepository; } public void Process() { .... } |
所以基本上我的理解是注册依赖项,然后我应该能够在我的类中注入它们。我不确定是否需要传递我的服务提供商?
我相信在MVC中,实现这一点是有魔力的。在不使用服务定位器模式的情况下如何进行注入?
基本上,您不需要将
您所能做的就是将依赖项添加到您的
当您遇到只需要加载它的一个子集的情况(即,当传递某个参数时使用不同的服务或程序逻辑),您将需要一种工厂(工厂是一种薄包装器,它在传递对象之前创建和配置对象,如果是IOC,它还解析依赖项)。
在工厂实现中,如果需要,可以引用容器(您需要范围依赖性或每个对象创建的临时解析)。如果需要多个
有两种方法。对于非常简单的工厂,您可以使用工厂方法,这可以在进行
1 2 3 4 | services.AddTransient<Task1>(); services.AddTransient<Func<Task1>>( (serviceProvider) => { return () => serviceProvider.GetService<Task1>(); }); |
然后注入你的依赖。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | 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 } } |
如果您需要更复杂的配置或带有运行时参数,那么创建工厂类可能更有意义。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | 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); } } |
及用法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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); } } |
这是我尝试在测试应用程序中使用您的代码,但我不确定是否正确执行了此操作。
我也不确定如何传入mytaskapplication createnewtask(connection string)中方法的连接字符串。
它是否需要作为MyTaskApplication的属性或构造函数的一部分或其他方法传入?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 | 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); } } |