".Net Core 3 控制台无法从 appsettings 获取连接字符串"

3

我创建了一个.Net Core 3.1控制台应用程序,尝试使用EntityFramework来操作我的Oracle数据库。我找到了一些视频和文章,但它们都是处理HTTP应用程序而不是控制台应用程序。

我使用PowerShell命令生成表并创建了一个名为 ModelContext DbContext。在我的Program.cs中,我使用Host.CreateDefaultBuilder添加services.AddDbContext,并将连接字符串放入appsettings.json文件中。

我的问题是,在尝试将上下文添加到主机中的服务时,无法从appsettings中获取连接字符串。在Configuration.GetConnectionString处出现设计时错误,显示“Configuration不包含‘GetConnectionString’的定义”。

我通过NuGet安装了System.Configuration.ConfigurationManager并在我的Program.cs文件中添加了using System.Configuration

如何从主机构建器中获取appsettings中的连接字符串?

Program.cs

var host = Host.CreateDefaultBuilder().ConfigureServices((context, services) =>
                {
                    services.AddTransient<IAppHost, AppHost>();
                    services.AddTransient<IFileManager, FileManager>();
                    services.AddTransient<IDataManager, DataManager>();

                    var connstring = Configuration.GetConnectionString("DbConnection");
                    services.AddDbContext<ModelContext>(options =>
                    {
                        options.UseOracle(connstring);
                    });

                    services.AddLogging(builder =>
                    {
                        builder.AddNLog("nlog.config");
                    });
                }).Build();

更新的代码

这里是我的Program.cs文件中所有的代码。不幸的是,我不确定我做了什么导致了这个问题,现在我的FileManager类出现了错误。

Unable to resolve service for type 'Microsoft.Extensions.Logging.Logger`1[EmailUpdateExport.FileManager]' while attempting to activate 'EmailUpdateExport.FileManager'.

我已经撤销了从刚刚添加的appsettings中获取DbConnection的代码,但依然出现错误。

class Program
        {
            static void Main(string[] args)
            {
                //Calls the Builder so that you can get to the appsettings.
                var builder = new ConfigurationBuilder();
                BuildConfig(builder);
    
                var logger = NLog.Web.NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();
    
                try
                {
                    logger.Debug("Initializing Program.Main");
                    
                    var host = Host.CreateDefaultBuilder()
                        .ConfigureAppConfiguration((hostingContext, config) =>
                        {
                            config.SetBasePath(Path.Combine(AppContext.BaseDirectory));
                            config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
                        })
                        .ConfigureServices((context, services) =>
                        {
                            services.AddTransient<IAppHost, AppHost>();
                            services.AddTransient<IFileManager, FileManager>();
                            services.AddTransient<IDataManager, DataManager>();
    
                            var connstring = context.Configuration["ConnectionStrings:DbConnection"];
                            services.AddDbContext<ModelContext>(options =>
                            {
                                options.UseOracle(connstring);
                            });
    
                            services.AddLogging(builder =>
                            {
                                builder.AddNLog("nlog.config");
                            });
                        }).Build();
    
    
                    //Create instance of AppHost and call the Run() function to run the business logic.
                    var svc = ActivatorUtilities.CreateInstance<AppHost>(host.Services);
                    svc.Run();
                }
                catch (Exception ex)
                {
                    //NLog: catch setup errors
                    logger.Error("Stopped program setup with Error. {0} | {1} | {2}", ex.Message, ex.StackTrace, ex.InnerException);
                    throw;
                }
                finally
                {
                    // Ensure to flush and stop internal timers/threads before application-exit
                    NLog.LogManager.Shutdown();
                }
            }
    
            static void BuildConfig(IConfigurationBuilder builder)
            {
                //Sets up the ability to talk to the appsettings.json 
                builder.SetBasePath(Directory.GetCurrentDirectory())
                    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                    .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true)
                    .AddEnvironmentVariables();
            }
    
        }

这是我的文件管理器,如果有建议,请提出来。

public class FileManager : IFileManager
    {
        private readonly ILogger<FileManager> _log;
        private readonly IConfiguration _configuration;

        public FileManager(Logger<FileManager> log, IConfiguration config)
        {
            _log = log;
            _configuration = config;
        }

        public void CreateFileDirectory(string FilePath)
        {
            try
            {
                //create the target location if it doesn't exist
                if (!Directory.Exists(FilePath))
                {
                    _log.LogInformation("Create directory: " + FilePath);
                    Directory.CreateDirectory(FilePath);
                }
            }
            catch (Exception ex)
            {
                _log.LogError("Error creating Export directory. {0} | {1} | {2} ", ex.Message, ex.StackTrace, ex.InnerException);
            }
        }


        public void CopyFile(string sourceLocation, string destinationLocation, string fileName)
        {
            _log.LogInformation("Source location: {0} | Destination location: {1} | File Name: {2}", sourceLocation, destinationLocation, fileName);

            var sourceFile = Path.Combine(sourceLocation, fileName);
            var destinationFile = Path.Combine(destinationLocation, fileName);

            _log.LogInformation("SourceFilePath: {0}", sourceFile);
            _log.LogInformation("DestinationFilePath: {0}", destinationFile);

            try
            {
                //check to make sure source exists first
                if (File.Exists(sourceFile))
                {
                    //get rid of the file if it already exists. Shouldn't be an issue most to the time.
                    if (File.Exists(destinationFile))
                    {
                        File.Delete(destinationFile);
                    }
                    File.Copy(sourceFile, destinationFile);
                }
                else
                    _log.LogInformation("Source file does not exist. File: {0}", sourceFile);

            }
            catch (Exception ex)
            {
                _log.LogError("Error copying file. Source: {0} | Destination: {1}. {2} | {3} | {4}", sourceFile, destinationFile, ex.Message, ex.StackTrace, ex.InnerException);
                throw;
            }
                
    }

请展示您添加/构建“配置”代码的位置。 - Roman
看一下这个,还有很多其他在线示例。https://garywoodfine.com/configuration-api-net-core-console-application/ - insane_developer
谢谢,我会看一下那篇文章,看看能否将其融入到我的代码中。 - Caverman
@RomanDoskoch 配置来自于 "using System.Configuration",该语句是从 NuGet 包 "System.Configuration.ConfigurationManager" 中引入的。 - Caverman
2
你为什么要使用System.Configuration.ConfigurationManager?那是用来处理app.config和web.config的,而不是appsettings.json。 - mason
4个回答

4
这是一种方法。该示例基于键名访问配置值。最好将此配置映射到一个类,以便检索设置时不容易出错。您可以查看我在评论中发布的链接,了解如何实现这一点。
var host = Host.CreateDefaultBuilder()
                .ConfigureAppConfiguration((hostingContext, config) =>
                {
                    config.SetBasePath(Path.Combine(AppContext.BaseDirectory));
                    config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true);
                })
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddDbContext<ModelContext>(options =>
                            {
                                options.UseSqlServer(hostContext.Configuration["ConnectionStrings:DefaultConnection"]);
                            }, ServiceLifetime.Transient)
                    // add other services
                })
                .UseConsoleLifetime()
                .Build();

appsettings.json:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=localhost;Database=myDB;Trusted_Connection=True;"
  },
  .....
}

这对我来说肯定有效。具体是什么错误? - insane_developer
我从那些据说可行的代码中看到的唯一区别是,在你的构造函数中,你使用了具体类而不是ILogger接口:public FileManager(Logger<FileManager> log, IConfiguration config)。虽然我知道这似乎不应该有什么区别。 - insane_developer
现在已经解决了,即使FileManager重新放置也没有问题。不确定FileManger出了什么问题,但是将其添加回来并按原样使用后就没有错误了。但是,我必须将硬编码的连接字符串添加回DbContext的OnConfiguring函数中。如何在DbContext的OnConfiguring函数中获取连接字符串? - Caverman
我不确定我理解你的问题。你是说,你不想在配置代码中设置连接字符串,而是想在DbContext类中设置吗?如果是这样的话,你有什么特别的原因觉得需要这样做吗? - insane_developer
我可以用任何一种方法来做到这一点...虽然在 .Net Framework 中它会在 DbContext 中获取连接字符串,所以我习惯于在那里使用它。当我使用你的示例时,我可以在它将连接添加到 services.AddDbContext 之前放一个断点,所以这样做可以工作。但是,如果我在 DbContext 中注释掉 OnConfiguring,我会收到一个没有数据库提供程序的错误。如果我取消对 DbContext 中硬编码的连接字符串的注释,它会起作用,但显然不是我要找的。 - Caverman
显示剩余6条评论

2

您需要构建正确的配置:

var configuration = new ConfigurationBuilder()
    .SetBasePath(Path.Combine(AppContext.BaseDirectory))
    .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) // add more configuration files if there is a need
    .Build();

然后使用它来获取连接字符串:

var connstring = configuration.GetConnectionString("DbConnection"); // use configuration object created by ConfigurationBuilder

0
无法解析类型为“Microsoft.Extensions.Logging.Logger`1[EmailUpdateExport.FileManager]”的服务,因此无法激活“EmailUpdateExport.FileManager”。
错误是由于您依赖注入了错误的服务,将您的代码更改如下(将Logger更改为ILogger):
public FileManager(ILogger<FileManager> log, IConfiguration config)
{
    _log = log;
    _configuration = config;
}

0

分享一下我最终找到适合我的方法。我实际上很喜欢这里,因为它在DbContext中,这也是.Net Framework获取连接字符串的位置。这与我过去使用的位置类似。希望这也能帮助其他人。

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                IConfigurationRoot configuration = new ConfigurationBuilder()
                     .SetBasePath(Path.Combine(Directory.GetCurrentDirectory()))
                     .AddJsonFile("appsettings.json", optional: false)
                     .Build();
                optionsBuilder.UseOracle(configuration.GetConnectionString("DbConnection"));

            }
        }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接