Configuration files for Windows Forms (VB.NET)
Introduction
Developers building conventional .NET desktop solutions typically store information needed while running an application using My.Settings, which provides read/write capabilities. Here, learn how to use a Json file to store read-only information while discarding write back to the Json file. If there is a need to modify the Json file consider using Microsoft’s System.Text.Json or Newton.Json libraries to save modifications.
Note
Several of the projects in the GitHub repository are .NET 5 along with several projects using Entity Framework Core. When building the projects and one or more fail because the developer machine does not have .NET 5 and/or Entity Framework Core consider unloading those projects.
Working with My.Setting
Creating and working with settings in Visual Studio, open project properties to the Settings tab. From here see Microsoft documentation.
For the above, Visual Studio adds these properties to app.config which after building a project names the file project name.exe.config.
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="userSettings"
type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="AppSettingsMySettings.My.MySettings"
type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<userSettings>
<AppSettingsMySettings.My.MySettings>
<setting name="ConnectionString" serializeAs="String">
<value>Data Source=DevServer;Initial Catalog=NorthWindAzure;Integrated Security=True</value>
</setting>
<setting name="WindowsAuthentication" serializeAs="String">
<value>True</value>
</setting>
</AppSettingsMySettings.My.MySettings>
</userSettings>
</configuration>
Access these settings as follows
Rolling your own loader
As developer become seasoned, they learn rolling their own method to store and read setting is more reliable and provides more control while if the developer does not code in specific functionality it's not there.
Using My.Setting properties from the first example.
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="SettingsConfig" type="BasicAppConfig.AppSettngsLoader, BasicAppConfig" />
</configSections>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.8" />
</startup>
<SettingsConfig>
<ConnectionString>Data Source=DevServer;Initial Catalog=NorthWindAzure;Integrated Security=True</ConnectionString>
<WindowsAuthentication>true</WindowsAuthentication>
</SettingsConfig>
</configuration>
AppSettingsLoader is a class in a sample project (included in the source code) which requires a reference to System.Configuration.
01.Imports System.Configuration
02.Imports System.Xml
03.Imports System.Xml.Serialization
04.
05.Public Class AppSettngsLoader
06. Implements IConfigurationSectionHandler
07.
08. Public Function Create(parent As Object, configContext As Object, section As XmlNode) As Object _
09. Implements IConfigurationSectionHandler.Create
10.
11. If section Is Nothing Then
12. Throw New ArgumentNullException($"XMLNode passed in is null.")
13. End If
14.
15. Dim type = AppDomain.CurrentDomain.GetAssemblies().
16. SelectMany(Function(assembly) assembly.GetTypes()).
17. FirstOrDefault(Function(itemType) itemType.Name = section.Name)
18.
19. If type Is Nothing Then
20. Throw New ArgumentException($"Type with name {section.Name} couldn't be found.")
21. End If
22.
23. Dim ser As New XmlSerializer(type, New XmlRootAttribute(section.Name))
24.
25. Using reader As XmlReader = New XmlNodeReader(section)
26. Return ser.Deserialize(reader)
27. End Using
28.
29. End Function
30.
31.End Class
BasicAppConfig is the namespace for the current project which means if the project namespace changes this needs to change too.
A concrete class for storing the two properties in the configuration file.
1.Public Class SettingsConfig
2. Public Property ConnectionString() As String
3. Public Property WindowsAuthentication() As Boolean
4.
5. Public Overrides Function ToString() As String
6. Return $"{ConnectionString}, {WindowsAuthentication}"
7. End Function
8.End Class
To get the properties.
Dim config = DirectCast(ConfigurationManager.GetSection("SettingsConfig"), SettingsConfig)
Then for instance, get the ConnectionString.
config.ConnectionString
Using appSettings in app.config
Another method is using appSettings section in app.config.
<appSettings>
<add key="ConnectingString" value="Data Source=.\SQLEXPRESS;Initial Catalog=NorthWind2020;Integrated Security=True" />
</appSettings>
Example usage in a DbContext for a Entity Framework Core class.
- Line 10 variable is for the connection string from app.config
- Line 25 uses ConfigurationHelper class to read the connection string
- Line 29 assigns the connection string to the DbContextOptionsBuilder which provides Entity Framework what is needed to work with data.
01.Imports System.Configuration
02.Imports Contexts.Configurations
03.Imports Microsoft.EntityFrameworkCore
04.Imports Models
05.
06.Namespace Contexts
07. Partial Public Class NorthWindContext
08. Inherits DbContext
09.
10. Private _connectionString As String
11.
12. Public Sub New()
13.
14. End Sub
15.
16. Public Sub New(options As DbContextOptions(Of NorthWindContext))
17. MyBase.New(options)
18. End Sub
19.
20. Public Overridable Property ContactType() As DbSet(Of ContactType)
21. Public Overridable Property Contacts() As DbSet(Of Contacts)
22.
23. Protected Overrides Sub OnConfiguring(optionsBuilder As DbContextOptionsBuilder)
24. If Not optionsBuilder.IsConfigured Then
25. Dim configurationHelper = New ConfigurationHelper
26. _connectionString = configurationHelper.ConnectionString
27.
28. If Not optionsBuilder.IsConfigured Then
29. optionsBuilder.UseSqlServer(_connectionString)
30. End If
31.
32. End If
33. End Sub
34.
35. Protected Overrides Sub OnModelCreating(modelBuilder As ModelBuilder)
36.
37. modelBuilder.ApplyConfiguration(New ContactTypeConfiguration())
38. modelBuilder.ApplyConfiguration(New ContactsConfiguration())
39.
40. OnModelCreatingPartial(modelBuilder)
41.
42. End Sub
43.
44. Partial Private Sub OnModelCreatingPartial(modelBuilder As ModelBuilder)
45. End Sub
46. End Class
47.End Namespace
Entity Framework Core self-contained connection
General practice is to use appsettings.json
{
"database": {
"DatabaseServer": ".\\SQLEXPRESS",
"Catalog": "NorthWind2020",
"IntegratedSecurity": "true",
"UsingLogging": "true"
}
}
Then to read the connection string from appsettings.json in the DbContext.
Create a private method
Private Shared Function BuildConnection() As String
Dim configuration = (New ConfigurationBuilder()).AddJsonFile("appsettings.json", True, True).Build()
Dim sections = configuration.GetSection("database").GetChildren().ToList()
Return $"Data Source={sections(1).Value};Initial Catalog={sections(0).Value};Integrated Security={sections(2).Value}"
End Function
Which is called in OnConfiguring method.
Protected Overrides Sub OnConfiguring(optionsBuilder As DbContextOptionsBuilder)
If Not optionsBuilder.IsConfigured Then
NormalConfiguration(optionsBuilder)
End If
End Sub
Going a step farther consider creating methods which BuildConnection is used along with setting up for logging and or events and interceptors as per below.
Private Sub NormalConfiguration(optionsBuilder As DbContextOptionsBuilder)
optionsBuilder.UseSqlServer(BuildConnection())
End Sub
Private Sub NormalConfigurationEnableDetailedErrors(optionsBuilder As DbContextOptionsBuilder)
optionsBuilder.
UseSqlServer(BuildConnection()).
LogTo(AddressOf Console.WriteLine).EnableDetailedErrors()
End Sub
Private Sub WithSaveChangesInterceptor(optionsBuilder As DbContextOptionsBuilder)
optionsBuilder.
AddInterceptors(New SavedChangesInterceptor).
UseSqlServer(BuildConnection())
End Sub
Private Sub WithCommandInterceptor(optionsBuilder As DbContextOptionsBuilder)
optionsBuilder.
AddInterceptors(New CommandInterceptor).
UseSqlServer(BuildConnection())
End Sub
Private Shared Sub LogQueryInfoToDebugOutputWindow(ByVal optionsBuilder As DbContextOptionsBuilder)
optionsBuilder.
UseSqlServer(BuildConnection()).
EnableSensitiveDataLogging().LogTo(Sub(message) Debug.WriteLine(message))
End Sub
Then for multiple environments expand appsettings as per below. Environment could be an Enum where each member is numbered for dev, staging, prod environments instead of a Boolean.
{
"ConnectionStrings": {
"DevelopmentConnection": "Server=.\\SQLEXPRESS;Database=NorthWind2020;Integrated Security=true",
"ProductionConnection": "Server=ProdServerDoesNotExists;Database=NorthWind2020Prod;Integrated Security=true"
},
"Environment": {
"Production": false
}
}
Custom configurations using appsettings.json
The appsettings.json file is a convenient way to store and retrieve your application’s configuration. For VB coders working with desktop solutions, there are no code samples other than ASP.NET Core hence this section will provide the core code to use appsettings.json (and note a different name can be used while sticking with appsettings.json is the standard).
NuGet packages
In Visual Studio, double click on a .NET Core project and and the following, save/close the file.
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.FileExtensions" Version="5.0.0" />
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="5.0.0" />
</ItemGroup>
Version numbers may change and if so under manage NuGet packages an alert will appear if there are newer versions which allows updating to a current version.
Next, add a appsetings.json file and set copy to output folder to copy if newer under properties for the file.
{
"Environment": {
"ConnectionString": "Data Source=DevServer;Initial Catalog=NorthWindAzure;Integrated Security=True",
"MailTo": "karen.payne@someDomain.com;fred.smith@comcast.net;billAdams@gmail.com",
"UseGeoLocation": true
}
}
Add the following class
- The constructor makes a call to Setup which in turn invokes GetConfiguration for telling .NET where to find the configuration file
- Lines 46,47,48 retrieve setting and cast as needed to the proper type defined by the read-only properties in ApplicationConfiguration.
01.Imports Microsoft.Extensions.Configuration
02.
03.Public NotInheritable Class ApplicationConfiguration
04. Private Shared ReadOnly Lazy As New _
05. Lazy(Of ApplicationConfiguration)(Function() New ApplicationConfiguration())
06.
07. ''' <summary>
08. ''' Access point to methods and properties
09. ''' </summary>
10. Public Shared ReadOnly Property Instance() As ApplicationConfiguration
11. Get
12. Return Lazy.Value
13. End Get
14. End Property
15.
16. Private Property Settings() As IConfiguration
17.
18. Public Property MailToAddressList() As List(Of String)
19.
20. Private Sub New()
21. SetUp()
22. End Sub
23. ''' <summary>
24. ''' Initialize configuration to read json file
25. ''' </summary>
26. ''' <returns></returns>
27. Private Function GetConfiguration() As IConfiguration
28.
29. Dim builder = (New ConfigurationBuilder()).
30. SetBasePath(AppContext.BaseDirectory).
31. AddJsonFile("appsettings.json",
32. [optional]:=True,
33. reloadOnChange:=True)
34.
35. Return builder.Build()
36.
37. End Function
38. ''' <summary>
39. ''' Get values from json file, assign to properties
40. ''' </summary>
41. Private Sub SetUp()
42.
43. MailToAddressList = New List(Of String)
44. Settings = GetConfiguration()
45.
46. _connectionString = Settings("Environment:ConnectionString")
47. _mailAddress = Settings("Environment:MailTo").Split(";"c).ToList()
48. _useGeoLocation = CType(Settings("Environment:UseGeoLocation"), Boolean)
49.
50. End Sub
51.
52. Private Shared _mailAddress As List(Of String)
53. ''' <summary>
54. ''' Mail addresses for sending error report
55. ''' </summary>
56. ''' <returns></returns>
57. Public ReadOnly Property MailAddresses() As List(Of String)
58. Get
59. Return _mailAddress
60. End Get
61. End Property
62. Private Shared _connectionString As String
63. ''' <summary>
64. ''' Database connection string
65. ''' </summary>
66. ''' <returns></returns>
67. Public ReadOnly Property ConnectionString() As String
68. Get
69. Return _connectionString
70. End Get
71. End Property
72.
73. Private Shared _useGeoLocation As Boolean
74. ''' <summary>
75. ''' Get current location
76. ''' </summary>
77. ''' <returns>True collect, False do not collect geo location</returns>
78. Public ReadOnly Property UseGeoLocation() As Boolean
79. Get
80. Return _useGeoLocation
81. End Get
82. End Property
83.End Class
Reading the ConnectionString
ApplicationConfiguration.Instance.ConnectionString
Read mail addresses
ApplicationConfiguration.Instance.MailAddresses
Read UseGeoLocation
Dim geoLocation As Boolean = ApplicationConfiguration.Instance.UseGeoLocation
In the above samples, all code is within the same project. If there will be many projects that used the same settings consider using a class project to read settings as presented in the following class project.
Complex settings
With reboust applications many times there will be a need to have multiple environments e.g. development, test and production. The following appsettings file is one fictitious example.
{
"Environment": {
"Name": "Development"
},
"GeneralSettings": [
{
"Environment": "Development",
"ReloadApplicationOnEveryRequest": true,
"Trace": false,
"Reload": "reload",
"Password": true,
"ConnectionString": "Data Source=DevServer;Initial Catalog=NorthWindAzure;Integrated Security=True",
"DiConfiguration": {
"Dsn": "ABC",
"Globals": "globals",
"Globals2": "globals2",
"MailTo": "karen.payne@someDomain.com;fred.smith@comcast.net;billAdams@gmail.com",
"ExitLink": "/ocs4/",
"OcsLink": null,
"MfLink": "",
"MfUser": null,
"MfPass": "",
"UseGeoLocation": true,
"ResetPinLocation": "/pinchange/begin/",
"BaseServerAddress": "xxx4",
"UirTakeTest": false,
"QryCacheShort": "00:00:10",
"QryCacheLong": "00:00:05"
}
},
{
"Environment": "Test",
"ReloadApplicationOnEveryRequest": false,
"Trace": false,
"Reload": "reload",
"Password": true,
"ConnectionString": "Data Source=TestServer;Initial Catalog=NorthWindAzure;Integrated Security=True",
"DiConfiguration": {
"Dsn": "DEF",
"Globals": "globals",
"Globals2": "globals2",
"MailTo": "karen.payne@someDomain.com;fred.smith@comcast.net;billAdams@gmail.com",
"ExitLink": "/ocs4/",
"OcsLink": null,
"MfLink": "",
"MfUser": null,
"MfPass": "",
"UseGeoLocation": true,
"ResetPinLocation": "/pinchange/begin/",
"BaseServerAddress": "xxx4",
"UirTakeTest": false,
"QryCacheShort": "00:10:00",
"QryCacheLong": "00:01:00"
}
},
{
"Environment": "Production",
"ReloadApplicationOnEveryRequest": false,
"Trace": false,
"Reload": "reload",
"Password": true,
"ConnectionString": "Data Source=ProductionServer;Initial Catalog=NorthWindAzure;Integrated Security=True",
"DiConfiguration": {
"Dsn": "GHI",
"Globals": "globals",
"Globals2": "globals2",
"MailTo": "karen.payne@someDomain.com;fred.smith@comcast.net;billAdams@gmail.com",
"ExitLink": "/ocs4/",
"OcsLink": null,
"MfLink": "",
"MfUser": null,
"MfPass": "",
"UseGeoLocation": true,
"ResetPinLocation": "/pinchange/begin/",
"BaseServerAddress": "xxx4",
"UirTakeTest": false,
"QryCacheShort": "06:00:00",
"QryCacheLong": "00:30:00",
"ConnectionString": "Prod connection"
}
}
]
}
To create this, see the following project and read the readme.md file for instructions. Now create a new .NET Console or Windows Form project, add a reference to this project which contains classes and methods to read settings. This is followed by adding appsettings.json, add the Json above, set copy to output folder to copy if newer under properties for the file.
The following shows reading settings, note that there are custom console method helpers included in ConsoleHelper project.
Imports Classes
Imports ConsoleHelpers
Module Program
Sub Main(args As String())
WriteHeader("Configuration code sample")
Console.WriteLine($"Environment (Singleton): {ApplicationSettings.Instance.Environment}")
Console.WriteLine($" Environment: {Helper.Environment}")
Console.WriteLine($" Dsn: {ApplicationSettings.Instance.Dsn}")
Console.WriteLine($" Connection string: {ApplicationSettings.Instance.ConnectionString}")
EmptyLine()
Dim mailAddress = ApplicationSettings.Instance.MailAddresses
Console.WriteLine("Mail addresses")
For Each address In mailAddress
Console.WriteLine($"{vbTab}{address}")
Next
EmptyLine()
Console.WriteLine($"QryCacheShort: {ApplicationSettings.Instance.QryCacheShort}")
ReadLineWithTimeoutAndMessage()
End Sub
End Module
Included projects
Rather than step through each project here take time to examine all the projects as each one has something different to offer. Start with Basic2 followed by Basic1/ConfigurationHelper together.
Summary
Two distinct ways to read settings from files have been presented with the focus on moving to appsettings.json over app.config when creating .NET Core desktop solutions with plenty of sample code to get started.
See also
Entity Framework Core 3.x database connection
.NET Core desktop application configurations (C#)
Source code
Use Visual Studio or Git-Desktop to clone the following GitHub repository.