configuration.GetValue list returns null - c#

I am trying to read a list from appsettings.json file using the GetValue<T> method:
var builder = new ConfigurationBuilder().SetBasePath(System.AppContext.BaseDirectory)
.AddJsonFile("appsettings.json", optional: false, reloadOnChange: true);
IConfigurationRoot configuration = builder.Build();
var rr = configuration.GetValue<IList<ConnectionSettings>>("Connections");
public class ConnectionSettings
{
public string Name { get; set; }
public string Host { get; set; }
public string Account { get; set; }
public string Password { get; set; }
}
and my appsettings.json
{
"Connections": [
{
"Name": "",
"Host": "192.168.1.5",
"Account": "74687",
"Password": "asdsdadsq"
},
{
"Name": "",
"Host": "127.0.0.1",
"Account": "45654",
"Password": "asdasads"
}
]
}
Problem is that I always get null and I dont understand why.

According to the documentation for GetValue<>, it gets the value of a (single) key and converts it to the specified type. Unfortunately, it doesn't throw an error if the value can't be converted, which is the situation you're running into.
I believe that Get<> would be preferable in your situation.
var rr = configuration.GetSection("Connections").Get<IList<ConnectionSettings>>();
According to Get<>'s documentation, it:
Attempts to bind the configuration instance to a new instance of type T. If this configuration section has a value, that will be used. Otherwise binding by matching property names against configuration keys recursively.
This allows you to get the value directly or, if it can't find the property, it looks for nested objects that contain a matching property.
An alternative would be as #AthanasiosKataras says; use Bind<>. This is helpful when you might have a sparse configuration in which you want to overlay some values with either default or calculated values.

I have spotted the following issue on GitHub: GetValue<T> not working with lists
Long story short: It is by design.
So you can try this:
var result = new List<ConnectionSettings>();
var rr = configuration.GetSection("Connections").Bind(result);

Configuration.Get<T> is a better option when you have nested configuration using non-primitive structure like list or array.
{
"Email": {
"ToEmails": [
"test1#test.com",
"test2#test.com",
"test3#test.com"
]
}
List<string> emailTo = _config.GetSection("Email:ToEmails").Get<List<string>>()
foreach (string email in emailTo)
{
sendGridMessage.AddTo(new EmailAddress(email));
}
OR use Bind()
public static class ConfigurationRootExtentions
{
public static List<T> GetListValue<T>(this IConfigurationRoot configurationRoot, string section)
{
var result = new List<T>();
configurationRoot.GetSection(section).Bind(result);
return result;
}
}
Ref[1]: https://blog.bitscry.com/2017/11/14/reading-lists-from-appsettings-json/
Ref[2]: https://github.com/aspnet/Configuration/issues/451

If you develop a project from scratch i.e using the VS template then set the following properties for appsettings.json (Right-click on appsettings.json--> click on Properties ). After that update following property value.
Copy to output Directory: Copy always
Now, you can run the application and get value using GetValue method.
After that, You can reverse the same setting as pervious
Copy to output Directory: Copy if newer

It might be useful for you.
GetValue<type>("key") does not work for complex types.
Let's say you have the following appsettings.json structure:
{
"ArrayOfObjectsWithinObjects": [
{
"foo0": "bar",
"foo1": 1,
"foo2": false,
"anotherObject": {
"foo3.1": "3.1",
"foo3.2": "3.2"
}
},
{
"foo0": "bar",
"foo1": 1,
"foo2": false,
"anotherObject": {
"foo3.1": "3.1",
"foo3.2": "3.2"
}
}
...
]
}
and C# the appropriate classes
public class Foo
{
public string foo0 { get; set;}
public int foo1 { get; set;}
public bool foo2 { get; set;}
public AnotherObject anotherObject { get; set;}
}
public class AnotherObject
{
public string foo3.1 { get; set; }
public string foo3.2 { get; set; }
}
You should be able to:
//using static System.Console;
//using System.Linq;
//using Microsoft.Extensions.Configuration;
var _listOfFoos = _config.GetSection("ArrayOfObjects")
.GetChildren()
.ToList()
.Select( x => new Foo
{
x.GetValue<string>("")
x.GetValue<int>("")
x.GetValue<bool>("")
x.GetSection("anotherObject").Get<AnotherObject>()
});
WriteLine(_listOfFoos.anotherObject.foo3.1);
Note that foo3.1 is an invalid c# syntax and was used for didactic purposes.

Related

How to get two key values of the configuration JSON using GetSection

I have the following JSON format in my appsetting:
"AzureServiceBusTopic": {
"TopicsToConsume": [
{
"SubscriptionName": "sub1",
"TopicName": "topic1"
},
{
"SubscriptionName": "sub2",
"TopicName": "topic2"
}
]
}
Currently I'm getting SubscriptionName and TopicName separately:
var topicToConsumeConfig = this.configuration.GetSection("AzureServiceBusTopic:TopicsToConsume").GetChildren();
this.topicToConsume = topicToConsumeConfig.Select(x => x.GetSection("TopicsToConsume").Value);
this.subscriptionName = topicToConsumeConfig.Select(x => x.GetSection("SubscriptionName").Value);
But I need to have the first subscriptionName and topicName together, for the second one likewise.
How can I get it?
I want to get these two:
[0]: {
"SubscriptionName": "sub1",
"TopicName": "topic1"
},
[1]: {
"SubscriptionName": "sub2",
"TopicName": "topic2"
}
You can do something like this:
private static List<T> GetList<T>(IConfiguration configuration, string configSection, string keyName)
{
var result = new List<T>();
configuration.GetSection($"{configSection}:{keyName}").Bind(result);
return result;
}
then you can call it like this:
var myListOfTopics = GetList<TopicToConsume>(this.configuration, "AzureServiceBusTopic", "TopicsToConsume");
Obviously you need to create a class to hold the "Topic" object like this:
public class TopicToConsume
{
public string SubscriptionName { get; set; }
public string TopicName { get; set; }
}
You might need to reference nuget Microsoft.Extensions.Configuration.Binder
which gives you access to the Bind extension method.
Also notice that because of the object is a json array [] it comes out as a a List.
But in any case with this code you can pull the whole "list of topics" in one single GetSection call
BONUS:
You can use the same technique to bind dictionaries. So you can really get complex with it.
I made it into a static method that can easily be turned into an extension method.

Get value of a class property by JsonProperty

For environment settings, we are storing JSON blobs in our DB, such as:
[
{
"env": "local",
"keya": "valueA_local"
"keyb": "valueB_local"
},
{
"env": "development",
"keya": "valueA_dev"
"keyb": "valueB_dev"
},
{
"env": "qa",
"keya": "valueA_qa"
"keyb": "valueB_qa"
}
]
And we have an enum:
public enum EnvironmentSettings
{
KeyA = 1,
KeyB = 2,
}
And we have a class:
[Serializable]
public class MyProjectEnvironmentSettings
{
[JsonProperty("env")]
public string Environment { get; set; }
[JsonProperty("keya")]
public string KeyA{ get; set; }
[JsonProperty("keyb")]
public string KeyB{ get; set; }
}
And finally, I need to create a method that returns a value for a given pair. I'd like this method to not have to be modified if a new setting is added.
public string GetEnvironmentSetting(EnvironmentSettings environmentSetting)
{
List<MyProjectEnvironmentSettings> environmentSettings = // get the entire list from the DB
string envName = // get environment
string keyToGet = environmentSetting.ToString().ToLower();
// This will get the group of settings specific to the environment
var envSettings = environmentSettings.Where(e => e.Environment.ToLower() == envName).FirstOrDefault();
// And last, I need to get the value for the specific setting that is being requested
string settingValue = envSettings.Where(e => e.???? == keyToGet ); // <---------- This is where I need help!
}
I'm unsure of how to query by the properties JsonProperty attribute.
Thanks!
You can use reflection.
var property = typeof(MyProjectEnvironmentSettings)
.GetProperties()
.FirstOrDefault(prop => prop
.GetCustomAttribute<JsonPropertyAttribute>()?.PropertyName == keyToGet);
string settingValue = property?.GetValue(envSettings);
You may need to add BindingFlags if you change the accessibility of your properties.
This way you won't have to change your GetEnvironmentSetting method, but you will still need to update your enum and your class's properties each time you add something.
Also, you will still have the burden of making sure the names match. You could also create a mapping between enum and properties (even directly to the PropertyInfo, so you could cache them), but that then creates a third thing that you have to maintain for each change.

Dependency Injection with options pattern

I'm trying to load some settings from the appsettings file and I've got a small issue with the way lists are loaded when using the options pattern. Suppose I've got the following classes (for loading the settings):
public class Application {
public string Name { get; set; } = "";
public IEnumerable<string> Roles { get; set; } = new[] {""};
public Application ToApplicationWithoutPass() =>
new Application {
Name = Name,
Username = Username,
Roles = Roles.ToList()
};
}
public class Applications {
public IEnumerable<Application> AppList { get; set; } = new List<Application>();
}
And here is what the settings that are defined on the appsetings file look like:
"Applications": {
"AppList": [
{
"Name": "SraWebuserAdmin",
"Roles": [ "SraEntitiesWriters", "SraEntitiesReaders", "SraEntitiesLoginAccess" ]
},
...
Here are the entries from the DI setup which is done on the ConfigureServices method:
services.Configure<Applications>(options => Configuration.GetSection("Applications").Bind(options));
services.AddScoped<IApplicationAccessVerifier, ApplicationAccessVerifier>();
And, finally, here's the constructor of the ApplicationAccessVerifier class:
public ApplicationAccessVerifier(IOptionsSnapshot<Applications> applicationOptions) {
_applicationOptions = applicationOptions;
}
Now, the question: if I don't initialize the AppList property,
public class Applications {
public IEnumerable<Application> AppList { get; set; }
}
then the settings are loaded correctly.
However, if I initialized it like I've shown (making sure the filed wrapper by the property is initialized with an empty list), then the settings won't be copied to the AppList.
I find this strange since simple properties (ex.: Name on the Application class) aren't affected by the same issue.
Can anyone tell me why this happens or point me to an official documentation about it?

Injectable ApplicationConfig service

I want a service I can inject - or in my example get with GetService - that contains settings from my appsettings.json file.
The appsettings.json fragment looks like this:
"ExternalInterfaces": [
{
"Name": "name1",
"BaseUrl": "https://www.baseurl1.svc"
},
{
"Name": "name2",
"BaseUrl": "https://www.baseurl2.svc"
}
]
To do this I have the following interfaces:
using System.Collections.Generic;
namespace Infrastructure.Settings
{
public interface IExternalInterfaceSettingsCollection
{
IReadOnlyCollection<IExternalInterfaceSettings> Settings { get; set; }
}
}
namespace Infrastructure.Settings
{
public interface IExternalInterfaceSettings
{
string Name { get; set; }
string BaseUrl { get; set; }
}
}
and the following corresponding classes:
using System.Collections.Generic;
namespace Infrastructure.Settings
{
public class ExternalInterfaceSettingsCollection : IExternalInterfaceSettingsCollection
{
public IReadOnlyCollection<IExternalInterfaceSettings> Settings { get; set; }
}
}
namespace Infrastructure.Settings
{
public class ExternalInterfaceSettings : IExternalInterfaceSettings
{
const string DefaultName = "newExternalInterface";
const string DefaultBaseUrl = "";
public string Name { get; set; } = DefaultName;
public string BaseUrl { get; set; } = DefaultBaseUrl;
}
}
And in my Startup.cs I have this (definitely gets called with no exceptions):
services.Configure<IExternalInterfaceSettingsCollection>(settings => _configuration.GetSection("ExternalInterfaces").Bind(settings));
and this is then consumed as follows:
var externalInterfaceConfiguration = app.ApplicationServices.GetService<ExternalInterfaceSettingsCollection>();
var Setting1BaseUrl = externalInterfaceConfiguration.Settings
.SingleOrDefault(s => s.Name == "name1")?.BaseUrl;
However, in the last 3 lines, externalInterfaceConfiguration is always null.
I'm clearly missing something, but I can't see what. Any clues?
You've registered IExternalInterfaceSettings, but you're attempting to retrieve ExternalInterfaceSettings. There's no such service in the collection, so the result is null (since you used GetService<T>). If you had used GetRequiredService<T> then an exception would have been thrown telling you as much.
Then, the options pattern is not meant to bind to interfaces. The whole idea is that you're binding to a POCO that represents a specific set of settings. If you want to use an interface, I suppose that's your prerogative, but it's not going to be applicable to the options configuration. In other words, you need the following instead:
services.Configure<ExternalInterfaceSettings>(Configuration.GetSection("ExternalInterfaces"));
(Note, the action overload with Bind is superfluous. You can just pass the config section directly.)
With that, you'll be able to request something like IOptions<ExternalInterfaceSettings>, but you still cannot get ExternalInterfaceSettings directly from the service collection. If you want that functionality, you'll need to add an additional service registration (which can utilize an interface, this time):
services.AddScoped<IExternalInterfaceSettings, ExternalInterfaceSettings>(p =>
p.GetRequiredService<IOptions<ExternalInterfaceSettings>>().Value);

Web API 2 - Implementing a PATCH

I currently have a Web API that implements a RESTFul API. The model for my API looks like this:
public class Member
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime Created { get; set; }
public DateTime BirthDate { get; set; }
public bool IsDeleted { get; set; }
}
I've implemented a PUT method for updating a row similar to this (for brevity, I've omitted some non-relevant stuff):
[Route("{id}")]
[HttpPut]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
// Do some error checking
// ...
// ...
var myDatabaseEntity = new BusinessLayer.Member(id);
myDatabaseEntity.FirstName = model.FirstName;
myDatabaseEntity.LastName = model.LastName;
myDatabaseEntity.Created = model.Created;
myDatabaseEntity.BirthDate = model.BirthDate;
myDatabaseEntity.IsDeleted = model.IsDeleted;
await myDatabaseEntity.SaveAsync();
}
Using PostMan, I can send the following JSON and everything works fine:
{
firstName: "Sara",
lastName: "Smith",
created: "2018/05/10",
birthDate: "1977/09/12",
isDeleted: false
}
If I send this as my body to http://localhost:8311/api/v1/Member/12 as a PUT request, the record in my data with ID of 12 gets updated to what you see in the JSON.
What I would like to do though is implement a PATCH verb where I can do partial updates. If Sara gets married, I would like to be able to send this JSON:
{
lastName: "Jones"
}
I would like to be able to send just that JSON and update JUST the LastName field and leave all the other fields alone.
I tried this:
[Route("{id}")]
[HttpPatch]
public async System.Threading.Tasks.Task<HttpResponseMessage> UpdateRow(int id,
[FromBody]Models.Member model)
{
}
My problem is that this returns all the fields in the model object (all of them are nulls except the LastName field), which makes sense since I am saying I want a Models.Member object. What I would like to know is if there is a way to detect which properties have actually been sent in the JSON request so I can update just those fields?
I hope this helps using Microsoft JsonPatchDocument:
.Net Core 2.1 Patch Action into a Controller:
[HttpPatch("{id}")]
public IActionResult Patch(int id, [FromBody]JsonPatchDocument<Node> value)
{
try
{
//nodes collection is an in memory list of nodes for this example
var result = nodes.FirstOrDefault(n => n.Id == id);
if (result == null)
{
return BadRequest();
}
value.ApplyTo(result, ModelState);//result gets the values from the patch request
return NoContent();
}
catch (Exception ex)
{
return StatusCode(StatusCodes.Status500InternalServerError, ex);
}
}
Node Model class:
[DataContract(Name ="Node")]
public class Node
{
[DataMember(Name = "id")]
public int Id { get; set; }
[DataMember(Name = "node_id")]
public int Node_id { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "full_name")]
public string Full_name { get; set; }
}
A valid Patch JSon to update just the "full_name" and the "node_id" properties will be an array of operations like:
[
{ "op": "replace", "path": "full_name", "value": "NewNameWithPatch"},
{ "op": "replace", "path": "node_id", "value": 10}
]
As you can see "op" is the operation you would like to perform, the most common one is "replace" which will just set the existing value of that property for the new one, but there are others:
[
{ "op": "test", "path": "property_name", "value": "value" },
{ "op": "remove", "path": "property_name" },
{ "op": "add", "path": "property_name", "value": [ "value1", "value2" ] },
{ "op": "replace", "path": "property_name", "value": 12 },
{ "op": "move", "from": "property_name", "path": "other_property_name" },
{ "op": "copy", "from": "property_name", "path": "other_property_name" }
]
Here is an extensions method I built based on the Patch ("replace") specification in C# using reflection that you can use to serialize any object to perform a Patch ("replace") operation, you can also pass the desired Encoding and it will return the HttpContent (StringContent) ready to be sent to httpClient.PatchAsync(endPoint, httpContent):
public static StringContent ToPatchJsonContent(this object node, Encoding enc = null)
{
List<PatchObject> patchObjectsCollection = new List<PatchObject>();
foreach (var prop in node.GetType().GetProperties())
{
var patch = new PatchObject{ Op = "replace", Path = prop.Name , Value = prop.GetValue(node) };
patchObjectsCollection.Add(patch);
}
MemoryStream payloadStream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(patchObjectsCollection.GetType());
serializer.WriteObject(payloadStream, patchObjectsCollection);
Encoding encoding = enc ?? Encoding.UTF8;
var content = new StringContent(Encoding.UTF8.GetString(payloadStream.ToArray()), encoding, "application/json");
return content;
}
}
Noticed that tt also uses this class I created to serialize the PatchObject using DataContractJsonSerializer:
[DataContract(Name = "PatchObject")]
class PatchObject
{
[DataMember(Name = "op")]
public string Op { get; set; }
[DataMember(Name = "path")]
public string Path { get; set; }
[DataMember(Name = "value")]
public object Value { get; set; }
}
A C# example of how to use the extension method and invoking the Patch request using HttpClient:
var nodeToPatch = new { Name = "TestPatch", Private = true };//You can use anonymous type
HttpContent content = nodeToPatch.ToPatchJsonContent();//Invoke the extension method to serialize the object
HttpClient httpClient = new HttpClient();
string endPoint = "https://localhost:44320/api/nodes/1";
var response = httpClient.PatchAsync(endPoint, content).Result;
Thanks
PATCH operations aren't usually defined using the same model as the POST or PUT operations exactly for that reason: How do you differentiate between a null, and a don't change. From the IETF:
With PATCH, however, the enclosed entity contains a set of
instructions describing how a resource currently residing on the
origin server should be modified to produce a new version.
You can look here for their PATCH suggestion, but sumarilly is:
[
{ "op": "test", "path": "/a/b/c", "value": "foo" },
{ "op": "remove", "path": "/a/b/c" },
{ "op": "add", "path": "/a/b/c", "value": [ "foo", "bar" ] },
{ "op": "replace", "path": "/a/b/c", "value": 42 },
{ "op": "move", "from": "/a/b/c", "path": "/a/b/d" },
{ "op": "copy", "from": "/a/b/d", "path": "/a/b/e" }
]
#Tipx's answer re using PATCH is spot on, but as you've probably already found, actually achieving that in a statically typed language like C# is a non-trivial exercise.
In the case where you're using a PATCH to represent a set of partial updates for a single domain entity (e.g. to update the first name and last name only for a contact with many more properties) you need to do something along the lines of looping each instruction in the 'PATCH' request and then applying that instruction to an instance of your class.
Applying an individual instruction will then comprise of
Finding the property of the instance that matches the name in the
instruction, or handling property names you weren't expecting
For an update: Trying to parse the value submitted in the patch into the instance property and handling the error if e.g. the instance property is a bool but the patch instruction contains a date
Deciding what to do with Add instructions as you can't add new properties to a statically typed C# class. One approach is to say that Add means "set the value of the instance's property only if property's existing value is null"
For Web API 2 on the full .NET Framework the JSONPatch github project looks to make a stab at providing this code, although it doesn't look like there's been a lot of development on that repo recently and the readme does state:
This is still very much an early project, don't use it in production
yet unless you understand the source and don't mind fixing a few bugs
;)
Things are simpler on .NET Core as that has a set of functionality to support this in the Microsoft.AspNetCore.JsonPatch namespace.
The rather useful jsonpatch.com site also lists out a few more options for Patch in .NET:
Asp.Net Core JsonPatch (Microsoft official implementation)
Ramone (a framework for consuming REST services, includes a JSON Patch implementation)
JsonPatch (Adds JSON Patch support to ASP.NET Web API)
Starcounter (In-memory Application Engine, uses JSON Patch with OT for client-server sync)
Nancy.JsonPatch (Adds JSON Patch support to NancyFX)
Manatee.Json (JSON-everything, including JSON Patch)
I need to add this functionality to an existing Web API 2 project of ours, so I'll update this answer if I find anything else that's useful while doing that.
I wanted to achieve exactly the same thing, but used a different method to others described here. I've created a working repo using this if you want to check it out:
https://github.com/emab/patch-example
If you have the following two models:
Database model
public class WeatherDBModel
{
[Key]
public int Id { get; set; }
public string City { get; set; }
public string Country { get; set; }
public double Temperature { get; set; }
public double WindSpeed { get; set; }
public double Rain { get; set; }
public Weather(int id, string city, string country, double temperature, double windSpeed, double rain)
{
Id = id;
City = city;
Country = country;
Temperature = temperature;
WindSpeed = windSpeed;
Rain = rain;
}
}
Update model
Containing exact names of database model properties. Includes properties which can be updated
public class WeatherUpdateModel
{
public string? City { get; set; }
public string? Country { get; set; }
public double Temperature { get; set; }
public double WindSpeed { get; set; }
public double Rain { get; set; }
}
This update model is sent to the service layer along with the id of the object you'd like to update.
You can then implement the following method in your repository layer which maps any non-null values from the updateModel into an existing entity if it has been found:
public Weather Update(int id, WeatherUpdate updateObject)
{
// find existing entity
var existingEntity = _context.Weather.Find(id);
// handle not found
if (existingEntity == null)
{
throw new EntityNotFoundException(id);
}
// iterate through all of the properties of the update object
// in this example it includes all properties apart from `id`
foreach (PropertyInfo prop in updateObject.GetType().GetProperties())
{
// check if the property has been set in the updateObject
// if it is null we ignore it. If you want to allow null values to be set, you could add a flag to the update object to allow specific nulls
if (prop.GetValue(updateObject) != null)
{
// if it has been set update the existing entity value
existingEntity.GetType().GetProperty(prop.Name)?.SetValue(existingEntity, prop.GetValue(updateObject));
}
}
_context.SaveChanges();
return existingEntity;
}
Using this method you can change your models without worrying about the update logic, as long as you ensure that the UpdateModel is kept up-to-date with the database model.
If a property of your object was omitted in your JSON, ASP.NET won't "set" that property on the object, the property will have its default value. In order to know which properties were sent with the JSON object you need to have a way to detect which properties of the object were set.
In order to detect which properties have "actually been sent" with the JSON object, you can modify your Member class to contain a collection of property names that were "set". Then, for all properties that you want to be able to know if they were sent in the JSON object make that when the property is set the name of the property should be added to the collection of set properties.
public class Member
{
private string _firstName;
private string _lastName;
...
private bool _isDeleted;
public string FirstName
{
get => _firstName;
set
{
_firstName = value;
_setProperties.Add(nameof(FirstName));
}
}
public string LastName
{
get => _lastName;
set
{
_lastName = value;
_setProperties.Add(nameof(LastName));
}
}
...
public bool IsDeleted
{
get => _isDeleted;
set
{
_isDeleted= value;
_setProperties.Add(nameof(IsDeleted));
}
}
private readonly HashSet<string> _setProperties = new HashSet<string>();
public HashSet<string> GetTheSetProperties()
{
return new HashSet<string>(_setProperties);
}
}
In the UpdateRow method you can now check whether a property was sent in the JSON by checking if it is in the _setProperties collection. So if you want to see if the LastName was sent in the JSON just do
bool lastNameWasInJson = model.Contains(nameof(model.LastName));
Following up to Avid Learners approach. I found this easy to add to an existing PUT method.
Alternatively to avoid loading twice you could apply update operations and then before saving apply the patch, but I'd rather load twice and have simple code.
public ResultModel Patch(UpdateModel model)
{
var record = LoadAsUpdateModel(model.Id);
if (record == null) return null;
foreach(var propertyName in model.SetProperties())
{
var property = model.GetType().GetProperty(propertyName);
property.SetValue(record, property.GetValue(model));
}
return Update(record);
}

Categories