了解控制台应用程序中的.net Core依赖注入

了解控制台应用程序中的.net Core依赖注入

问题描述:

控制台应用程序不像Web应用程序那样将启动文件与配置服务一起使用,我正在努力理解依赖注入的关键概念。

Console apps don't use the Startup file with configure services like web apps do and I'm struggling to understand the crucial concept of Dependency Injection.

(请请注意以下示例不会编译)

(Please note the below example does not compile)

以下是我认为应如何工作的基本示例(请指出任何非常规或错误的内容):

Here is a basic example of how I think it should work (please do point out anything unconventional or wrong):

        static void Main(string[] args)
        {
            var builder = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                .AddUserSecrets<Settings>()
                .Build();

            var services = new ServiceCollection()
                .AddLogging(b => b
                    .AddConsole())
                .AddDbContext<UnderstandingDIContext>(options =>
                    options.UseSqlite(builder.GetConnectionString("DefaultConnection")))
                .BuildServiceProvider();

            var logger = services.GetService<ILoggerFactory>()
                .CreateLogger<Program>();

            logger.LogInformation("Starting Application");

            var worker = new Worker();

            logger.LogInformation("Closing Application");
        }

但是如何在工人类中使用这些服务? / p>

But how do I use these services inside my 'Worker' class?:

        public Worker(ILogger logger, IConfiguration configuration)
        {
            logger.LogInformation("Inside Worker Class");
            var settings = new Settings()
            {
                Secret1 = configuration["Settings:Secret1"],
                Secret2 = configuration["Settings:Secret2"]
            };
            logger.LogInformation($"Secret 1 is '{settings.Secret1}'");
            logger.LogInformation($"Secret 2 is '{settings.Secret2}'");

            using (var context = new UnderstandingDIContext())
            {
                context.Add(new UnderstandingDIModel()
                {
                    Message = "Adding a message to the database."
                });
            }
        }

UnderstandingDIContext

UnderstandingDIContext

    public class UnderstandingDIContext : DbContext
    {
        public UnderstandingDIContext(DbContextOptions<UnderstandingDIContext> options)
            : base(options)
        { }

        public DbSet<UnderstandingDIModel> UnderstandingDITable { get; set; }
    }

此代码的问题如下:

Worker()希望传递ILogger和IConfiguration参数,但我认为依赖注入应该可以解决?

Worker() is expecting to be passed ILogger and IConfiguration parameters but I thought Dependency Injection should cover that?

我无法运行'dotnet ef migrations add Initial',因为我没有正确传递连接字符串(错误:无法创建类型为 UnderstandingDIContext的对象。)

I cannot run 'dotnet ef migrations add Initial' because I'm not correctly passing in the connection string (error: 'Unable to create an object of type 'UnderstandingDIContext'.')

(var context = new理解DIContext())不会编译,因为我误解了DbContext位。

'using (var context = new UnderstandingDIContext())' won't compile because I'm misunderstanding the DbContext bit.

我在很多地方进行搜索,并且有很多示例对于Web应用程序,但对于控制台应用程序则很少。我只是完全误解了依赖注入的整个概念吗?

I've searched around A LOT and there's lots of examples for web apps but very little for Console apps. Am I just completely misunderstanding the entire concept of Dependency Injection?

使用构造函数注入时,依赖关系只能在对象您正在创建的实际上是通过 依赖项注入本身创建的。因此,使依赖注入在您的 Worker 中起作用的关键是实际上也通过依赖注入容器解析 Worker

When using constructor injection, dependencies will only be resolved when the object you are creating is actually created through dependency injection itself. So the key to make dependency injection work within your Worker is to actually resolve Worker through the dependency injection container as well.

这实际上很简单:

var services = new ServiceCollection()
    .AddLogging(b => b.AddConsole())
    .AddDbContext<UnderstandingDIContext>(options =>
        options.UseSqlite(builder.GetConnectionString("DefaultConnection")));

// register `Worker` in the service collection
services.AddTransient<Worker>();

// build the service provider
var serviceProvider = services.BuildServiceProvider();

// resolve a `Worker` from the service provider
var worker = serviceProvider.GetService<Worker>();

var logger = serviceProvider.GetService<ILogger<Program>>();
logger.LogInformation("Starting Application");

worker.Run();

logger.LogInformation("Closing Application");

此外,由于您使用的数据库上下文已注册为作用域默认情况下为em>依赖项,我建议您也创建一个服务范围,或者在注册时更改数据库上下文的生存期。

In addition, since you are using a database context which gets registered as a scoped dependency by default, I would recommend you to create a service scope as well—or alternatively change the lifetime of the database context when you register it.

var serviceProvider = services.BuildServiceProvider();

using (var scope = serviceProvider.CreateScope())
{
    var worker = serviceProvider.GetService<Worker>();
    worker.Run();
}

请注意,我还做了一个显式方法 Run 对您的worker的使用,这样您就无需在构造函数中使用逻辑。

Note that I also made an explicit method Run on your worker, so that you don’t have the logic within the constructor.

public class Worker
{
    private readonly ILogger<Worker> _logger = logger;
    private readonly IConfiguration _configuration = configuration;
    private readonly UnderstandingDIContext _dbContext = dbContext;

    public Worker(ILogger<Worker> logger, IConfiguration configuration, UnderstandingDIContext dbContext)
    {
        _logger = logger;
        _configuration = configuration;
        _dbContext = dbContext;
    }

    public void Run()
    {
        _logger.LogInformation("Inside Worker Class");
        var settings = new Settings()
        {
            Secret1 = configuration["Settings:Secret1"],
            Secret2 = configuration["Settings:Secret2"]
        };

        _logger.LogInformation($"Secret 1 is '{settings.Secret1}'");
        _logger.LogInformation($"Secret 2 is '{settings.Secret2}'");

        _dbContext.Add(new UnderstandingDIModel()
        {
            Message = "Adding a message to the database."
        });
        _dbContext.SaveChanges();
    }
}