【问题标题】:Understanding .net Core Dependency Injection in a console app了解控制台应用程序中的 .net Core 依赖注入
【发布时间】:2019-05-04 14:29:52
【问题描述】:

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

(请注意以下示例无法编译)

这是我认为它应该如何工作的一个基本示例(请指出任何非常规或错误的地方):

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

但是如何在我的 'Worker' 类中使用这些服务呢?:

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

了解DIContext

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

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

这段代码的问题如下:

Worker() 期望传递 ILogger 和 IConfiguration 参数,但我认为依赖注入应该涵盖这一点?

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

'using (var context = new UnderstandingDIContext())' 无法编译,因为我误解了 DbContext 位。

我搜索了很多,有很多关于 Web 应用程序的示例,但对于控制台应用程序却很少。我是不是完全误解了依赖注入的整个概念?

【问题讨论】:

  • 不要自己创建new Worker()。而是将其注册到您的 ServiceCollection 并让 DI 为您实例化它。
  • 这是否意味着您必须实现一个接口(例如 IWorker)或者这是推荐的做法?
  • 不,你可以注册一个没有接口的依赖。只需将具体类型放在注册调用和构造函数参数中即可。

标签: c# asp.net-core dependency-injection


【解决方案1】:

当使用构造函数注入时,依赖关系只会在你创建的对象被真正创建通过依赖注入本身时被解析。因此,在 Worker 中实现依赖注入的关键是通过依赖注入容器实际解析 Worker

这其实很简单:

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

此外,由于您使用的是默认注册为 scoped 依赖项的数据库上下文,因此我建议您也创建一个服务范围,或者更改数据库的生命周期注册时的上下文。

var serviceProvider = services.BuildServiceProvider();

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

请注意,我还在您的工作人员上创建了一个显式方法Run,因此您在构造函数中没有逻辑。

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

【讨论】:

  • 经过更多研究、尝试不同的事情并在上述 cmets 中 @Dai 的建议的帮助下,我几乎到了那里,并且非常接近您在此处发布的代码,但这已得到很好的解释和演示,因此您打勾。非常感谢您的宝贵时间。
  • 尝试创建数据库迁移时出现错误“无法创建类型为“UnderstandingDIContext”的对象。 - 一切都正确编译并且连接字符串被成功读取。我错过了一些简单的东西吗?我觉得这与 Sqlite 中的 Database.EnsureCreate() 有关,但不知道该放在哪里。
  • @mwade 您如何尝试创建数据库迁移?您可能需要设置 design time context factory 才能使用 EF 工具。
  • 使用 CLI 命令“dotnet ef migrations add Create”。为什么这不起作用? services.AddDbContext&lt;UnderstandingDIContext&gt;(options =&gt; options.UseSqlite(builder.GetConnectionString("DefaultConnection"))); 使用时似乎没有正确注入:public UnderstandingDIContext(DbContextOptions&lt;UnderstandingDIContext&gt; options) : base(options) { } 我能找到的唯一方法是在 OnConfiguring 方法上手动放入连接字符串,而不是从 appsettings.json 中获取。跨度>
【解决方案2】:

看看这个 API。 https://github.com/Akeraiotitasoft/ConsoleDriving Nuget:Akeraiotitasoft.ConsoleDriving

【讨论】:

  • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-11-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-06-30
  • 1970-01-01
  • 2020-07-20
相关资源
最近更新 更多