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 14 great preprocessors for developers who love to code Sometimes it seems like the rules of programming are designed to make coding a chore. Here are 14 ways preprocessors can help make software development fun again. By Peter Wayner Nov 18, 2024 10 mins Development Tools Software Development feature Designing the APIs that accidentally power businesses Well-designed APIs, even those often-neglected internal APIs, make developers more productive and businesses more agile. By Jean Yang Nov 18, 2024 6 mins APIs Software Development news Spin 3.0 supports polyglot development using Wasm components Fermyon’s open source framework for building server-side WebAssembly apps allows developers to compose apps from components created with different languages. By Paul Krill Nov 18, 2024 2 mins Microservices Serverless Computing Development Libraries and Frameworks news Go language evolving for future hardware, AI workloads The Go team is working to adapt Go to large multicore systems, the latest hardware instructions, and the needs of developers of large-scale AI systems. By Paul Krill Nov 15, 2024 3 mins Google Go Generative AI Programming Languages Resources Videos