Take advantage of init-only setters to make configuration options immutable in ASP.NET Core MVC 5 to avoid unnecessary assignments and eliminate opportunities for errors. Credit: Thinkstock Immutability makes code easier to write, test, and maintain over time. However, immutability is not supported by many programming languages. Until recently, C# did not support immutability out-of-the-box. That changes with C# 9. You can now take advantage of init-only properties to configure your application options as immutable instances. After all, they are basically application constants. You wouldn’t want them to be changed during the lifetime of the application. This article discusses immutable objects, why we might want to make our configuration information immutable, and how this can be achieved in ASP.NET Core MVC 5. To work with the code examples provided in this article, you should have Visual Studio 2019 installed in your system. If you don’t already have a copy, you can download Visual Studio 2019 here. Create an ASP.NET Core MVC 5 project in Visual Studio 2019 First off, let’s create an ASP.NET Core project in Visual Studio. Following these steps will create a new ASP.NET Core MVC 5 project in Visual Studio 2019. Launch the Visual Studio IDE. Click on “Create new project.” In the “Create new project” window, select “ASP.NET Core Web App (Model-View-Controller)” from the list of templates displayed. Click Next. In the “Configure your new project” window, specify the name and location for the new project. Optionally check the “Place solution and project in the same directory” check box, depending on your preferences. Click Next. In the “Additional Information” window shown next, select .NET 5.0 as the target framework from the drop-down list at the top. Leave the “Authentication Type” as “None” (default). Ensure that the check boxes “Enable Docker,” “Configure for HTTPS,” and “Enable Razor runtime compilation” are unchecked, as we won’t be using any of those features here. Click Create. A new ASP.NET Core MVC 5 project will be created. We’ll use this project in the subsequent sections of this article. What is an immutable object? An immutable object is defined as an object that cannot be changed after it has been created. A string is an example of an immutable type in C#. You cannot change a string object after it has been created. When you attempt to change a string object, a new string instance is created in memory with the new data inside. By contrast, an instance of StringBuilder is a mutable object. A StringBuilder in C# is a mutable sequence of characters that can be expanded to store more characters if needed. Unlike changing a string, changing a StringBuilder instance does not create a new instance in memory. For many use cases, such as a database configuration metadata class, immutability is a desirable feature. Another use case for immutability is a data transfer object (DTO). An instance of a DTO is often serialized so that it can be independent of the technology used at the consumer end. Naturally, when transferring a data object between a database and a client, you would like to ensure that the object cannot be changed — and that is exactly the purpose of a DTO. You also have immutable collections in C#. Immutable collections are collections of types whose members cannot be changed after they have been created. To work with immutable collections, you should install the System.Collection.Immutable NuGet package in your project. You can read more about immutable collections in my earlier article here. Make IOptions immutable in ASP.NET Core MVC 5 When working in ASP.NET Core MVC 5 you will often store your application’s settings in a file such as appsettings.json and then read them back whenever the application needs them. To read those settings in your application, you can take advantage of dependency injection in the ConfigureServices method of the Startup class using IOptions. Now consider the following code snippet that shows a class called DbConfiguration, which is used to store database configuration information. public class DbConfiguration { public string Server { get; init; } public string Provider { get; init; } public string Database { get; init; } public int Port { get; init; } public string UserName { get; init; } public string Password { get; init; } } Note the declarations of the init-only setters for each of the database configuration properties above. Init-only setters allow you to set these values in the usual way, but these properties become read-only once their initial construction has completed. You can specify the database configuration information in the appsettings.json file as shown below. { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "DbConfiguration": { "Server": "Server01", "Provider": "Provider01", "Database": "Database01", "Port": 1433, "UserName": "User01", "Password": "Password01" }, "AllowedHosts": "*" } The following code snippet shows how you can bind the database configuration information specified in the appsettings.json file to an instance of DbConfiguration. services.Configure<DbConfiguration> (options => Configuration.GetSection("DbConfiguration").Bind(options)); Alternatively, you can use the following code to Bind and then add the database configuration instance as a singleton service. var dbConfiguration = new DbConfiguration(); Configuration.Bind("DbConfiguration", dbConfiguration); services.AddSingleton(dbConfiguration); Here is the complete code of the ConfigureServices method for your reference: public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); services.Configure<DbConfiguration> (options => Configuration.GetSection("DbConfiguration").Bind(options)); } Read configuration data in the controller in ASP.NET Core MVC 5 In the HomeController class, declare a read-only instance variable of type DbConfiguration as shown below. private readonly DbConfiguration _dbConfiguration; To access the DbConfiguration instance from the controller, we’ll take advantage of construction injection as shown in the following code snippet. public HomeController(ILogger<HomeController> logger, IOptions<DbConfiguration> dbConfiguration) { _logger = logger; _dbConfiguration = dbConfiguration.Value; } Note the usage of the IOptions instance in the parameter of the constructor above. The DbConfiguration instance named _dbConfiguration is initialized in the constructor using the Value property. If you now attempt to assign a value to any of the init-only properties or indexers of the DbConfiguration instance, you will be presented with the compile-time error message shown in Figure 1 below. IDG Figure 1. And that’s exactly what we wanted to achieve. The DbConfiguration instance named _dbConfiguration is immutable. You can read values from its properties but you can’t change the value of any of its properties in any way. Starting with C# 9, you can use init accessors in lieu of set accessors for properties and indexers. Doing so will ensure that these properties are read-only. You can only assign values to those properties at the time of creating the instance of the class they belong to. How to do more in ASP.NET Core: Dependency injection best practices for ASP.NET Core MVC 5 How to use security headers in ASP.NET Core MVC 5 How to handle unknown actions in ASP.NET Core 5 MVC How to overload action methods in ASP.NET Core 5 MVC How to use multiple implementations of an interface in ASP.NET Core How to use IHttpClientFactory in ASP.NET Core How to use the ProblemDetails middleware in ASP.NET Core How to create route constraints in ASP.NET Core How to manage user secrets in ASP.NET Core How to build gRPC applications in ASP.NET Core How to redirect a request in ASP.NET Core How to use attribute routing in ASP.NET Core How to pass parameters to action methods in ASP.NET Core MVC How to use API Analyzers in ASP.NET Core How to use route data tokens in ASP.NET Core How to use API versioning in ASP.NET Core How to use Data Transfer Objects in ASP.NET Core 3.1 How to handle 404 errors in ASP.NET Core MVC How to use dependency injection in action filters in ASP.NET Core 3.1 How to use the options pattern in ASP.NET Core How to use endpoint routing in ASP.NET Core 3.0 MVC How to export data to Excel in ASP.NET Core 3.0 Related content feature What is Rust? Safe, fast, and easy software development Unlike most programming languages, Rust doesn't make you choose between speed, safety, and ease of use. Find out how Rust delivers better code with fewer compromises, and a few downsides to consider before learning Rust. By Serdar Yegulalp Nov 20, 2024 11 mins Rust Programming Languages Software Development how-to Kotlin for Java developers: Classes and coroutines Kotlin was designed to bring more flexibility and flow to programming in the JVM. Here's an in-depth look at how Kotlin makes working with classes and objects easier and introduces coroutines to modernize concurrency. By Matthew Tyson Nov 20, 2024 9 mins Java Kotlin Programming Languages analysis Azure AI Foundry tools for changes in AI applications Microsoft’s launch of Azure AI Foundry at Ignite 2024 signals a welcome shift from chatbots to agents and to using AI for business process automation. By Simon Bisson Nov 20, 2024 7 mins Microsoft Azure Generative AI Development Tools news Microsoft unveils imaging APIs for Windows Copilot Runtime Generative AI-backed APIs will allow developers to build image super resolution, image segmentation, object erase, and OCR capabilities into Windows applications. By Paul Krill Nov 19, 2024 2 mins Generative AI APIs Development Libraries and Frameworks Resources Videos