2

Ich bin ein wenig verwirrt darüber, wie Service-Locator zu vermeiden, wenn eine Konsolenanwendungasp.net Kerndienst-Locator, wie in cosole Anwendung zu vermeiden

Programm

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

CommandLineOptions mit

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

Ich muss das IExampleRepository in das neue injizieren Task1()

task1

public class Task1 
{ 
    public Task1() 
    { 

    } 

    private readonly IExampleRepository _exampleRepository; 

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


    public void Process() { 
     .... 
    } 

Also im Grunde mein Verständnis ist, dass ich meine Abhängigkeiten registrieren, dann sollte ich in der Lage sein, sie während meiner Klassen zu injizieren. Ich bin mir nicht sicher, ob ich meinen ServiceProvider übergeben muss?

Ich glaube, dass es in MVC Magie gibt, die geschieht, um dies zu erreichen. Wie würde ich beim Injizieren vorgehen, ohne das Service-Locator-Muster zu verwenden?

Antwort

1

Grundsätzlich möchten Sie IServiceProvider in keiner Klasse außer dem Bootstrapper (Startup) oder den Factory-Methoden/Klassen übergeben, da dies Ihre Klassen an den spezifischen IoC-Container bindet.

Sie können Abhängigkeiten zu Ihrer CommandLineApplication Klasse hinzufügen und sie in der Main Methode auflösen und von hier aus können Sie Ihre Abhängigkeits-Injektionskette starten. Dies funktioniert so lange, wie Sie alle Ihre Abhängigkeiten auf einmal lösen müssen.

Wenn Sie in eine Situation kommen, in der Sie nur eine Teilmenge davon laden müssen (dh einen anderen Dienst oder eine Programmlogik verwenden, wenn ein bestimmter Parameter übergeben wird), benötigen Sie eine Fabrik (eine Fabrik ist eine Thin Wrapper, der ein Objekt erstellt und konfiguriert, bevor es übergeben wird, im Falle von IoC löst es auch die Abhängigkeiten).

In der Factory-Implementierung ist es in Ordnung, den Container zu referenzieren, falls erforderlich (Sie müssen abhängig von der Objekterstellung abhängige Abhängigkeiten oder vorübergehende Auflösung verwenden). Sie benötigen auch eine Fabrik, wenn Sie mehr als eine Instanz von Task1 benötigen.

Es gibt zwei Möglichkeiten. Für sehr einfache Fabriken können Sie eine Fabrikmethode verwenden, die direkt während Ihrer IServiceCollection Registrierungen verwendet werden kann.

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

dann in Ihre Abhängigkeit injizieren.

Wenn Sie eine komplexere Konfiguration oder einen Laufzeitparameter benötigen, kann es sinnvoller sein, eine Factory-Klasse zu erstellen.

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

Und die Nutzung

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); 
    } 
} 
+0

Vielen Dank für Ihre gründliche Erklärung, wäre zu viel Mühe, um ein kleines Beispiel für die Fabrik Implementierung zu fragen? Ich neige dazu, besser Beispiele zu lernen. Ich habe mir das angeschaut: http://stackoverflow.com/questions/557742/dependency-injection-vs-factory-pattern – DotnetShadow

+0

Zwei Beispiele für Factory-Methode bzw. Factory-Klasse hinzugefügt. – Tseng

+0

Schätze deine Bemühungen :) – DotnetShadow

0

Dies war mein Versuch, Ihren Code in einem Test-App verwenden, aber ich bin nicht sicher, ob ich das richtig mache.

Ich bin auch nicht sicher, wie in der Verbindungszeichenfolge für das Verfahren in MyTaskApplication CreateNewTask (Connection)

Wird es passieren muß als Eigentum übergeben werden, oder einen Teil des Konstruktor für MyTaskApplication oder Alternative Methode?

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); 
    } 
} 
+0

Ich denke, ich muss meine Aufgaben als AddTransient haben – DotnetShadow

Verwandte Themen