Multiple connections for appsettings.json (C#)

karenpayneoregon

Karen Payne

Posted on March 24, 2024

Multiple connections for appsettings.json (C#)

Introduction

Learn how to setup multiple connection strings for an application which is environmental independent. For example, there is a need to communicate to different database servers or two different catalogs on the same server without the need to modify a connection string.

As presented there are three connections, MainConnection, SecondaryConnection and OtherConnection. If the connection string names are not suitable, clone the repository, modify the code and use as a local NuGet package.

NuGet package GitHub repository

How to use in a project

Add ConsoleConfigurationLibrary NuGet package to your project. If this is your first time adding a package see Install and manage packages in Visual Studio using the NuGet Package Manager.

Add the following to startup code in the project.

await RegisterConnectionServices.Configure();
Enter fullscreen mode Exit fullscreen mode

Add appsettings.json file to a project, set copy to output directory to copy if newer.

{
  "ConnectionStrings": {
    "MainConnection": "",
    "SecondaryConnection": "",
    "OtherConnection": ""
  }
}
Enter fullscreen mode Exit fullscreen mode

Add your connection strings to each property above. See the sample here.

Setup a connection string.

Add the following using statement to the class or form to interact with databases.

using static ConsoleConfigurationLibrary.Classes.AppConnections;
Enter fullscreen mode Exit fullscreen mode

Next create a connection object, here its SQL-Server but works with all data providers.

using SqlConnection cn = new(Instance.MainConnection);
Enter fullscreen mode Exit fullscreen mode

Full example using conventional connection and command objects.

public static List<Track> TrackList(int albumId)
{

    using SqlConnection cn = new(Instance.MainConnection);
    using var cmd = new SqlCommand { Connection = cn, CommandText = SqlStatements.TracksByAlbumId };
    cmd.Parameters.AddWithValue("@AlbumId", albumId);

    List<Track> list = [];

    cn.Open();
    var reader = cmd.ExecuteReader();
    while (reader.Read())
    {
        Track track = new()
        {
            TrackId = reader.GetInt32(0),
            Name = reader.GetString(1),
            Milliseconds = reader.GetInt32(2)
        };
        list.Add(track);
    }


    return list;

}
Enter fullscreen mode Exit fullscreen mode

Full example use Dapper to read data.

public static List<Track> TrackListUsingDapper(int albumId)
{
    using SqlConnection cn = new(Instance.MainConnection);
    return cn.Query<Track>(SqlStatements.TracksByAlbumId, 
        new { AlbumId = albumId }).ToList();
}
Enter fullscreen mode Exit fullscreen mode

That is it to use the package.

Under the covers

The following class is responsible for configuration.

using ConsoleConfigurationLibrary.Models;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace ConsoleConfigurationLibrary.Classes;
public class ApplicationConfiguration
{
    /// <summary>
    /// Read sections from appsettings.json
    /// </summary>
    public static IConfigurationRoot ConfigurationRoot() =>
        new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false)
            .AddEnvironmentVariables()
            .Build();

    public static ServiceCollection ConfigureServices()
    {
        static void ConfigureService(IServiceCollection services)
        {
            services.Configure<ConnectionStrings>(ConfigurationRoot().GetSection(nameof(ConnectionStrings)));
            services.AddTransient<SetupServices>();
        }

        var services = new ServiceCollection();
        ConfigureService(services);

        return services;

    }
}
Enter fullscreen mode Exit fullscreen mode

Code to get the connection strings

public static class RegisterConnectionServices
{
    public static async Task Configure()
    {
        var services = ApplicationConfiguration.ConfigureServices();
        await using var serviceProvider = services.BuildServiceProvider();
        serviceProvider.GetService<SetupServices>()!.GetConnectionStrings();
    }
}
Enter fullscreen mode Exit fullscreen mode

The model for holding the three connections.

public sealed class AppConnections
{
    private static readonly Lazy<AppConnections> Lazy = new(() => new AppConnections());
    public static AppConnections Instance => Lazy.Value;
    public string MainConnection { get; set; }
    public string SecondaryConnection { get; set; }
    public string OtherConnection { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Entity Framework Core example

Add ConsoleConfigurationLibrary NuGet package to your project. If this is your first time adding a package see Install and manage packages in Visual Studio using the NuGet Package Manager.

Add the following to startup code in the project.

await RegisterConnectionServices.Configure();
Enter fullscreen mode Exit fullscreen mode

Add appsettings.json file to a project, set copy to output directory to copy if newer.

{
  "ConnectionStrings": {
    "MainConnection": "",
    "SecondaryConnection": "",
    "OtherConnection": ""
  }
}
Enter fullscreen mode Exit fullscreen mode

Set a connection string, in this sample we will use SecondaryConnection.

{
  "ConnectionStrings": {
    "MainConnection": "",
    "SecondaryConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=NorthWind2024;Integrated Security=True;Encrypt=False",
    "OtherConnection": ""
  }
}
Enter fullscreen mode Exit fullscreen mode

Add the following using to the DbContext class.

using static ConsoleConfigurationLibrary.Classes.AppConnections;
Enter fullscreen mode Exit fullscreen mode

Set the connection string in OnConfiguring.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) 
    => optionsBuilder.UseSqlServer(Instance.SecondaryConnection);
Enter fullscreen mode Exit fullscreen mode

Now lets read data.

using ConsoleConfigurationLibrary.Classes;
using EntityFrameworkCoreSampleApp.Data;

namespace EntityFrameworkCoreSampleApp;

internal partial class Program
{
    static async Task Main(string[] args)
    {
        await RegisterConnectionServices.Configure();
        await using var context = new Context();
        var list = context.Countries.ToList();
        foreach (var country in list)
        {
            Console.WriteLine($"{country.CountryIdentifier,-4}{country.Name}");
        }

        AnsiConsole.MarkupLine("[yellow]Press ENTER to quit[/]");
        Console.ReadLine();
    }
}
Enter fullscreen mode Exit fullscreen mode

Reading setting from app.config

Today its unlikely a developer will need to read settings from app.config as the default is to read from appsettings.json but there may be a case were a third party library requires an XML file for its configuration and since the code presented is not tied to a specific named file the code works, not tied to old ways of reading XML.

This is done with NuGet package Microsoft.Extensions.Configuration.Xml which allows reading settings using the same methods as used with json.

Here are the two base methods for reading XML and Json.

public class Configuration
{
    /// <summary>
    /// Setup to read appsettings.json
    /// </summary>
    public static IConfigurationRoot JsonRoot() =>
        new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json", optional: false)
            .AddEnvironmentVariables()
            .Build();

    /// <summary>
    /// Setup to read app.config
    /// </summary>
    public static IConfigurationRoot XmlRoot() =>
        new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddXmlFile("app.config", optional: true, reloadOnChange: true)
            .Build();

}
Enter fullscreen mode Exit fullscreen mode

An example to read settings.

<?xml version="1.0" encoding="utf-8" ?>
<configuration>

   <AppKey>XAS-1023-00-156</AppKey>

   <Position>
      <Title>Owner</Title>
      <Name>Jack Gibson</Name>
   </Position>

   <ConnectionString>
      <Production>Data Source=.\SQLEXPRESS;Initial Catalog=NW024;...</Production>
      <Development>Data Source=.\SQLEXPRESS;Initial Catalog=NW2023;...</Development>
   </ConnectionString>

   <Logging>
      <LogLevel>
         <Default>Information</Default>
         <Microsoft>Warning</Microsoft>
       </LogLevel>
   </Logging>

</configuration>
Enter fullscreen mode Exit fullscreen mode

Define models for reading the XML above. In each model, Position points to the location in app.config to read from.

Note on Logging which is multi level we use a colon to traverse to the settings.

public class ConnectionStringOptions
{
    public const string Position = "ConnectionString";
    public string Production { get; set; } = string.Empty;
    public string Development { get; set; } = string.Empty;
}
public class PositionOptions
{
    public const string Position = "Position";

    public string Title { get; set; } = string.Empty;
    public string Name { get; set; } = string.Empty;
}

public class Logging
{
    public const string Position = "Logging:LogLevel";

    public string Default { get; set; } = string.Empty;
    public string Microsoft { get; set; } = string.Empty;
}
Enter fullscreen mode Exit fullscreen mode

AppConfigService class has three methods to read settings.

public static class AppConfigService
{
    public static ConnectionStringOptions ConnectionStrings() =>
        Configuration.XmlRoot()
            .GetSection(ConnectionStringOptions.Position)
            .Get<ConnectionStringOptions>();

    public static PositionOptions GeneralSettings() =>
        Configuration.XmlRoot()
            .GetSection(PositionOptions.Position)
            .Get<PositionOptions>();

    public static string ApplicationKey() 
        => Configuration.XmlRoot()["AppKey"];

    public static Logging ApplicationLogging()
        => Configuration.XmlRoot().GetSection(Logging.Position)
            .Get<Logging>();
}
Enter fullscreen mode Exit fullscreen mode

The code above then allows access using the following.

ConnectionStringOptions connections = 
    AppConfigService.ConnectionStrings();

PositionOptions generalSettings = 
    AppConfigService.GeneralSettings();

var key = AppConfigService.ApplicationKey();

Logging appLogging = 
    AppConfigService.ApplicationLogging();
Enter fullscreen mode Exit fullscreen mode

Summary

With the provided package an application can have three distinct connections to different servers or three distinct connections to different catalogs on the same server or a mixture of both.

💖 💪 🙅 🚩
karenpayneoregon
Karen Payne

Posted on March 24, 2024

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Developer's format helpers
csharp Developer's format helpers

July 7, 2024

C# Generic Math
csharp C# Generic Math

February 26, 2023