.NET Core desktop application configurations (C#)
Introduction
In the .NET Framework prior to .NET Core application settings were in app.config file. All settings e.g. database connection, email setting etc in one file along with each setting are strings which need to be converted to proper types (see the following for conversions) plus some functionality is missing like smtp sections. Moving to .NET Core, the standard is using a Json file generally named appsettings.json. The intent here is to layout how to use appsettings.json for desktop applications.
There is also a secondary repository source (main source repository) to check out that works with both SqlClient and Entity Framework Core.
Update for ASP.NET Core
See the following NuGet package which works with .NET Core 5 and .NET Core 6
Base class project
By adding the following class project to a Visual Studio 2019 solution targeting .NET Core 5 with C#9 any application can read from appsettings.json or even a file structured similarly to separate different types of settings.
Example 1 appsettings.json for storing a single connection string in parts
{
"database": {
"DatabaseServer": ".\\SQLEXPRESS",
"Catalog": "School",
"IntegratedSecurity": "true",
"UsingLogging": "true"
}
}
Example 2 appsettings.json storing different connection strings for development and production environments with a setting indicate the environment.
{
"ConnectionStrings": {
"DevelopmentConnection": "Server=.\\SQLEXPRESS;Database=School;Integrated Security=true",
"ProductionConnection": "Server=ProdServerDoesNotExists;Database=School;Integrated Security=true"
},
"Environment": {
"Production": false
}
}
Example 3 appsettings.json with a single connection string along with email settings.
{
"GeneralSettings": {
"LogExceptions": true,
"DatabaseSettings": {
"DatabaseServer": ".\\SQLEXPRESS",
"Catalog": "School",
"IntegratedSecurity": true,
"UsingLogging": true
},
"EmailSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"DefaultCredentials": false,
"PickupDirectoryLocation": "MailDrop"
}
}
}
Example 4 Alternate/secondary configuration file, in this case named columnssettings.json. In this case there is an array which can be done also with any setting like with email/smtp settings in the last configuration file.
{
"GeneralSettings": {
"LogExceptions": true,
"DatabaseSettings": {
"DatabaseServer": ".\\SQLEXPRESS",
"Catalog": "School",
"IntegratedSecurity": true,
"UsingLogging": true
},
"EmailSettings": {
"Host": "smtp.gmail.com",
"Port": 587,
"EnableSsl": true,
"DefaultCredentials": false,
"PickupDirectoryLocation": "MailDrop"
}
}
}
Connection string Entity Framework Core 5
Out of the box when reverse engineering a database the connection string is hard coded in the DbContext which means when changing environments the connection string needs to be updated followed by rebuilding the project as shown below.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
#warning To protect potentially sensitive information in your connection string, you should move it out of source code. You can avoid scaffolding the connection string by using the Name= syntax to read it from configuration - see https://go.microsoft.com/fwlink/?linkid=2131148. For more guidance on storing connection strings, see http://go.microsoft.com/fwlink/?LinkId=723263.
optionsBuilder.UseSqlServer("Data Source=.\\SQLEXPRESS;Initial Catalog=School;Integrated Security=True");
}
}
Using the following no rebuilding is required, simply edit the configuration file and deploy to the selected environment.
In this example (using setting file in example 2 above) on line 11, Helper class is in the base class project, using GetConnectionString (there is also GetConnectionStringSecure for encrypted setting) which reads appsettings.json in the frontend project to select a connection string based off the Environment.Production, a bool project read from the configuration file.
Line 30 Helper.UseLogging determines if the DbContext should, in this case log to a text file.
01.namespace EntityLibrary.Data
02.{
03. public partial class SchoolContext : DbContext
04. {
05. /// <summary>
06. /// Connection string to interact with the database
07. /// </summary>
08. private static string _connectionString;
09. public SchoolContext()
10. {
11. _connectionString = Helper.GetConnectionString();
12. }
13.
14. public SchoolContext(DbContextOptions<SchoolContext> options) : base(options)
15. {
16. }
17.
18. public virtual DbSet<Person> Person { get; set; }
19.
20. /*
21. * Change the file name as desired along with a path if the file should be
22. * in another location than the application executable path.
23. */
24. private readonly StreamWriter _logStream = new StreamWriter("ef-log.txt", append: true);
25.
26. protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
27. {
28. if (!optionsBuilder.IsConfigured)
29. {
30. if (Helper.UseLogging())
31. {
32.
33. optionsBuilder.UseSqlServer(_connectionString)
34. .EnableSensitiveDataLogging()
35. .EnableDetailedErrors()
36. .LogTo(_logStream.WriteLine);
37.
38. }
39. else
40. {
41.
42. optionsBuilder.UseSqlServer(_connectionString);
43.
44. }
45.
46. }
47. }
48.
49. protected override void OnModelCreating(ModelBuilder modelBuilder)
50. {
51. modelBuilder.ApplyConfiguration(new PersonConfiguration());
52.
53. OnModelCreatingPartial(modelBuilder);
54. }
55.
56. partial void OnModelCreatingPartial(ModelBuilder modelBuilder);
57.
58. #region Takes care of disposing stream used for logging
59. public override void Dispose()
60. {
61. base.Dispose();
62. _logStream.Dispose();
63. }
64.
65. public override async ValueTask DisposeAsync()
66. {
67. await base.DisposeAsync();
68. await _logStream.DisposeAsync();
69. }
70. #endregion
71. }
72.}
For secured connection string Helper.GetConnectionStringSecure is used rather than GetConnectionString.
Use Securityhelper class project Writer and Reader methods to encrypt connection strings, or any string setting. At runtime the connection string is encrypted until needed.
1.{
2. "ConnectionStrings": {
3. "DevelopmentConnection": "bQ3FJ8OaAQM5XVQ2iGMQfsn+b1dGsCXyov+iLDCRgtO3tU/lLwVKgpfKshQj+9muOD0pwgcKoOKyl1uWNg/0vg=="
4. }
5.}
In the DbContext for obtaining a plain text connection string from a encrypted string.
1.public SchoolContext()
2.{
3. _connectionString = Helper.GetConnectionStringSecure();
4.}
Where without encryption
1.public SchoolContext()
2.{
3. _connectionString = Helper.GetConnectionString();
4.}
There is a test project to try out a secure connection string included.
01.namespace SecurityHelperConfiguration
02.{
03. class Program
04. {
05. /// <summary>
06. /// Example to create encrypted connection string for, in this case sql-server
07. /// </summary>
08. /// <param name="args"></param>
09. static void Main(string[] args)
10. {
11.
12. var plainText = "Server=.\\SQLEXPRESS;Database=School;Integrated Security=true";
13. Console.WriteLine($"Original: {plainText}");
14.
15. var encryptedText = ApplicationConfiguration.Writer(plainText);
16. Console.WriteLine($"Encrypted: {encryptedText}");
17.
18. var connectionString = ApplicationConfiguration.Reader(encryptedText);
19. Console.WriteLine($"Connection string: {connectionString}");
20.
21. Console.ReadLine();
22. }
23. }
24.}
Base class properties and methods
The following properties and methods are available.
Settings file defaults to appsettings.json, to change for a alternate settings file use ConfigurationFileName
1.public static string ConfigurationFileName { get; set; } = "appsettings.json";
Inner workings
The following method initializes were to get the json file.
01.private static IConfigurationRoot InitMainConfiguration()
02.{
03.
04. var builder = new ConfigurationBuilder()
05. .SetBasePath(Directory.GetCurrentDirectory())
06. .AddJsonFile(ConfigurationFileName);
07.
08. return builder.Build();
09.
10.}
Figure 1 Generic method to strong type settings
1.public static T InitOptions<T>(string section) where T : new()
2.{
3. var config = InitMainConfiguration();
4. return config.GetSection(section).Get<T>();
5.}
Figure 2 usage
01.public static string ConnectionString()
02.{
03.
04. InitMainConfiguration();
05. var applicationSettings = InitOptions<DatabaseSettings>("database");
06.
07. var connectionString =
08. $"Data Source={applicationSettings.DatabaseServer};" +
09. $"Initial Catalog={applicationSettings.Catalog};" +
10. "Integrated Security=True";
11.
12. return connectionString;
13.}
Figure 3 Another example
01.public static bool UseLogging()
02.{
03.
04. InitMainConfiguration();
05.
06. var applicationSettings = InitOptions<ApplicationSettings>("database");
07.
08. return applicationSettings.UsingLogging;
09.
10.}
Classes for setting are located in the ConfigurationHelper class.
Class for connections with multiple environments
01.public class ConnectionStrings
02.{
03. /// <summary>
04. /// Development environment connection string
05. /// </summary>
06. public string DevelopmentConnection { get; set; }
07. /// <summary>
08. /// Production environment connection string
09. /// </summary>
10. public string ProductionConnection { get; set; }
11. /// <summary>
12. /// true to use production, false to use test environment
13. /// </summary>
14. public bool IsProduction { get; set; }
15.}
Single connection with logging
01.public class DatabaseSettings
02.{
03. /// <summary>
04. /// Database server to work with
05. /// </summary>
06. public string DatabaseServer { get; set; }
07. /// <summary>
08. /// Initial catalog to data
09. /// </summary>
10. public string Catalog { get; set; }
11. /// <summary>
12. /// true for server authentication, false requires user name and password
13. /// </summary>
14. public bool IntegratedSecurity { get; set; }
15. /// <summary>
16. /// true to use production, false to use test environment
17. /// </summary>
18. public bool UsingLogging { get; set; }
19. /// <summary>
20. /// Connection string ready to use.
21. /// </summary>
22. public string ConnectionString => $"Data Source={DatabaseServer};" +
23. $"Initial Catalog={Catalog};" +
24. $"Integrated Security={IntegratedSecurity}";
25.
26.}
Email settings
1.public class EmailSettings
2.{
3. public string Host { get; set; }
4. public int Port { get; set; }
5. public bool EnableSsl { get; set; }
6. public bool DefaultCredentials { get; set; }
7. public string PickupDirectoryLocation { get; set; }
8.}
To implement settings that do not fit into the supplied classes above, add them to the Classes folder under the project ConfigurationHelper using figure 1 and 2 as models.
Summary
By using ConfigurationHelper class .NET Core 5 applications can use json files to store configuration settings from connection string, email settings to settings specific to an application.
Requires
- Microsoft Visual Studio 2019 or higher
- Projects setup for .NET Core 5 or higher
- C# 9, if using a lesser version modifications will be needed which need intermediate to advance level level skills to change.
Setting up source code provided
- Run the data script in a .sql file in Visual Studio or use SSMS (SQL-Server Management Studio)
- Build the solution, Visual Studio should auto-restore any missing NuGet Packages, if not run "restored NuGet packages" and build again.
See also
Entity Framework/Entity Framework Core dynamic connection strings
Entity Framework Core/Windows forms tips and tricks (using EF Core 3x)
C# Working with SQL-Server connection
GitHub resource
Moving from .NET Framework to .NET Core connections
Source code
Clone the following GitHub repository for full source with several code samples.