Консоль .Net Core 3 не может получить строку подключения из appsettings

#c# #asp.net-core #.net-core #dependency-injection

#c# #asp.net-core #.net-core #внедрение зависимостей

Вопрос:

Я создал консольное приложение .Net Core 3.1 и пытаюсь использовать EntityFramework для работы с моей базой данных Oracle. Я нашел несколько видео и статей на эту тему, но все они касаются HTTP-приложений, а не консольного приложения.

Я использовал команду powershell для построения таблиц и создания DbContext вызываемого ModelContext . В моем Program.cs у меня есть a Host.CreateDefaultBuilder и добавил s ervices.AddDbContext , а также поместил строку подключения в мой файл appsettings.json.

Моя проблема в том, что я не могу заставить его извлекать строку подключения из appsettings в тот момент, когда он пытается добавить контекст к службам на хосте. Я получаю ошибку времени разработки в конфигурации.Сообщение “Configuration does not contain a definition for ‘GetConnectionString’” GetConnectionString .

Я установил System.Configuration.ConfigurationManager через NuGet и добавил using System.Configuration в свой Program.cs файл. Как я могу получить строку подключения из 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. К сожалению, не уверен, что я сделал, чтобы вызвать это, но теперь я получаю сообщение об ошибке с моим классом FileManger.

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

Я даже откатил код для получения DbConnection из appsettings, которые я только что добавил, но все равно получаю ошибку.

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

Комментарии:

1. Пожалуйста, покажите код, в котором вы добавляете / создаете Configuration .

2. Взгляните на это, и в Интернете есть много других примеров. garywoodfine.com/configuration-api-net-core-console-application

3. Спасибо, я посмотрю на эту статью и посмотрю, смогу ли я включить ее в свой код.

4. @RomanDoskoch конфигурация поступает из «using System. Конфигурация «которая из системы NuGet package «. Конфигурация. ConfigurationManager «.

5. Почему вы используете System. Конфигурация. ConfigurationManager? Это для работы с app.config и web.config, а не с appsettings.json.

Ответ №1:

Это один из способов, которым вы можете это сделать. В этом примере доступ к значениям конфигурации осуществляется на основе имен ключей. Рекомендуется сопоставить эту конфигурацию с классом, чтобы получение настроек было менее подвержено ошибкам. Вы можете проверить ссылку, которую я разместил в своем комментарии, для получения подробной информации о том, как это сделать.

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

Комментарии:

1. Это приблизило меня. Теперь я могу получить строку подключения из appsettings перед добавлением DbContext, но я получаю другую ошибку с моим файловым менеджером. Не уверен, что я напутал, и я откатил код, который я добавил, чтобы получить строку подключения, и все равно получаю ошибку FileManager, так что это, вероятно, не связано. Мне придется отказаться от всего материала FileManger, чтобы посмотреть, смогу ли я заставить что-то работать. Снова разочарован этим… Core был для меня гораздо более сложным языком, чем любая из концепций ASP, с которыми я работал в прошлом.

2. Это, безусловно, работает для меня. В чем именно ошибка?

3. Единственное отличие, которое я вижу из кода, который предположительно работает, заключается в том, что в вашем конструкторе вы используете конкретный класс, а не ILogger интерфейс : public FileManager(Logger<FileManager> log, IConfiguration config) . Я знаю, что это не похоже на то, что это должно иметь значение.

4. Теперь он работает даже с возвращением FileManager на место. Не уверен, что происходит с FileManger, но добавил его обратно, как это было, и это не ошибка. Однако мне пришлось добавить обратно жестко запрограммированную строку подключения в функцию OnConfiguring DbContext. Как мне получить строку подключения внутри функции OnConfiguring DbContext?

5. Я не уверен, что понимаю, о чем вы спрашиваете. Вы хотите сказать, что не хотите устанавливать строку подключения в коде конфигурации, как в примере, а вместо этого в классе DbContext? Если да, есть ли у вас конкретная причина, по которой вы считаете, что вам нужно сделать это именно так?

Ответ №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
  

Ответ №3:

Не удалось разрешить службу для типа «Microsoft.Расширения.Ведение журнала.Logger`1[EmailUpdateExport.FileManager]’ при попытке активировать ‘EmailUpdateExport.FileManager’.

Ошибка связана с неправильным внедрением зависимостей, измените свой код, как показано ниже (измените Logger на ILogger ):

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

Ответ №4:

Публикую то, что я в конечном итоге обнаружил, что сработало для меня. Мне действительно нравится это здесь, так как оно находится в 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"));

            }
        }