Caverman
Caverman

Reputation: 3707

.Net Core 3 console not able to get connectionstring from appsettings

I’ve created a .Net Core 3.1 console app and I’m trying to use EntityFramework to work with my Oracle database. I’ve found several videos and articles on this subject but they all deal with HTTP apps and not a console app.

I used the powershell command to scaffold out the tables and create a DbContext called ModelContext. In my Program.cs I have a Host.CreateDefaultBuilder and added the services.AddDbContext as well as put the connectionstring in my appsettings.json file.

My issue is that I can’t get it to pull the connectionstring out of the appsettings at the point it’s trying to add the context to the services in the host. I get a design time error on the Configuration.GetConnectionString saying “Configuration does not contain a definition for ‘GetConnectionString’”.

I installed System.Configuration.ConfigurationManager through NuGet and add the using System.Configuration in my Program.cs file. How can I get the connectionstring from appsettings in my host builder?

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

UPDATED CODE

Here is all the code from my Program.cs file. Unfortunately, not sure what I've done to cause this but now I'm getting an error with my FileManger Class.

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

I've even backed out the code for getting the DbConnection from the appsettings that I just added but still getting the error.

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

Here is my FileManger in case someone has a suggestion with that.

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

Upvotes: 4

Views: 3817

Answers (4)

Caverman
Caverman

Reputation: 3707

Posting what I ultimately found that worked for me. I actually like it here since it's in the DbContext which is where .Net Framework gets it's connection string from as well. It's a similar location to what I've used in the past. Hopefully this helps someone else as well.

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

            }
        }

Upvotes: 1

Rena
Rena

Reputation: 36675

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

The error is because you dependency injection wrong service,change your code like below(change Logger to ILogger):

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

Upvotes: 0

Roman
Roman

Reputation: 12201

You need to build correct configuration:

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

and then use it to get connection string:

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

Upvotes: 3

insane_developer
insane_developer

Reputation: 819

This is one way you can do it. This example accesses the configuration values based on the key names. It's a good idea to map this configuration to a class, so retrieving settings is less prone to errors. You can check the link I posted in my comment for details about how to do that.

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

Upvotes: 4

Related Questions