Binding configuration section to POCO - defaulting to a default value - c#

I am trying to bind my object and have an invalid enum/bool. Essentially, any non-string variable will throw an exception (which I don't want). What I want is the a default value to be set (to null) instead of throwing an exception. Is there a way this can be achieved? Ideally I want to want to try parsing the string to an enum and is there a built in way (I haven't seen anything that achieves this).
Json File
// Json file
section: {
"Type": "Type3",
"SomeBool" : "falsee"
}
My config
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.AddEnvironmentVariables()
.Build();
// This throws an exception
var section = config.GetSection("section").Get<Section>();
POCO
public Section
{
public SomeType? Type { get; set; }
public bool? SomeBool { get; set; }
// Other props below
}
enum SomeType
{
Type1,
Type2
}

Related

.NET Core - The JSON property name for collides with another property

I'm migrating an old API to .net core web api and one of the responses includes the same value twice, so I'm using the native Json library of .NET 5 and I'm trying to get the same value twice in the JSON response, 'Id' and 'id'
{
...
"Id": "10",
"id": "10"
...
}
In my Startup, ConfigurationServices I configured the Json Option like this:
services.AddControllers().AddJsonOptions(options =>
{ options.JsonSerializerOptions.PropertyNameCaseInsensitive = true; });
My action method
[HttpGet]
public async Task<ActionResult<IEnumerable<object>>> GetContacts(string projectID)
{
Project project = _context.Projects.Where(a => a.Name == projectID)
.FirstOrDefault();
var contacts = await _context.Contacts.Where(a => a.ProjectId == project.Id)
.Select(o => new { id = o.Id, ID = o.Id}).ToListAsync();
return contacts;
}
While serializing, I am getting the "The JSON property name for collides with another property."
I think I'm missing something, and I'm stuck in this.
According to docs for PropertyNameCaseInsensitive:
Gets or sets a value that determines whether a property's name uses a case-insensitive comparison during deserialization.
So this flag is not about serialization and API output formatting. In the example code it is set to true. Hence, during deserialization a JSON property name should be matched with a single property of a target class in a case-insensitive manner. However, there is a clash - there are two candidate properties - Id and id. So it does not make sense.
Internally it's implemented as a case-insensitive dictionary for property lookup (decompiled .Net 5 by Rider):
public JsonClassInfo(Type type, JsonSerializerOptions options)
{
// ...
Dictionary<string, JsonPropertyInfo> cache = new Dictionary<string, JsonPropertyInfo>(
Options.PropertyNameCaseInsensitive
? StringComparer.OrdinalIgnoreCase
: StringComparer.Ordinal);
// ...
}
So the solution is to set PropertyNameCaseInsensitive to false and use PropertyNamingPolicy = JsonNamingPolicy.CamelCase (which is the default value and is omitted below):
public class SomeObject
{
[JsonPropertyName("Id")]
public int Id { get; set; }
public int id { get; set; }
public string SomeString { get; set; }
}
Startup:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddJsonOptions(options =>
options.JsonSerializerOptions.PropertyNameCaseInsensitive = false);
// ...
}
This should give:
{
"Id": 2,
"id": 3,
"someString": "..."
}

How to read a whole section from appsettings.json in ASP.NET Core 3.1?

I want to get a whole section from appsettings.json.
This is my appsettings.json:
{
"AppSettings": {
"LogsPath": "~/Logs",
"SecondPath": "~/SecondLogs"
}
}
C#:
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddJsonFile(this.SettingsFilesName);
configuration = builder.Build();
This syntax works fine and returns "~/Logs" :
configuration.GetSection("AppSettings:LogsPath");
But how can I have all "AppSettings" section? Is it possible?
This syntax doesn't work and value property is null.
configuration.GetSection("AppSettings");
UPDATE:
I have no model and read it in a class. I'm looking for something like this:
var all= configuration.GetSection("AppSettings");
and use it like
all["LogsPath"] or all["SecondPath"]
they return their values to me.
That is by design
With
var configSection = configuration.GetSection("AppSettings");
The configSection doesn't have a value, only a key and a path.
When GetSection returns a matching section, Value isn't populated. A Key and Path are returned when the section exists.
If for example you define a model to bind section data to
class AppSettings {
public string LogsPath { get; set; }
public string SecondPath{ get; set; }
}
and bind to the section
AppSettings settings = configuration.GetSection("AppSettings").Get<AppSettings>();
you would see that the entire section would be extracted and populate the model.
That is because the section will traverse its children and extract their values when populating the model based on matching property names to the keys in the section.
var configSection = configuration.GetSection("AppSettings");
var children = configSection.GetChildren();
Reference Configuration in ASP.NET Core

.Net Core Enum Values not serializing after getting the response

In .Net Web API core solution i have a class(Message) with variables of enum type as mentioned below
public enum MessageCode
{
[EnumMember]
NULL_PARAMETER,
BLANK_PARAMETER,
EMPTY_PARAMETER,
INVALID_PARAMETER,
PARAMETER_TRUNCATED,
QUERY_NOT_FOUND,
TERM_NOT_FOUND,
LIST_NOT_FOUND,
NO_SEARCH_RESULTS,
NO_UPDATES,
NO_DICTIONARY,
NO_PERMISSION,
LOCKED_PROTOCOL,
NO_TERMS_IN_LIST,
DUPLICATE_TERM
}
public enum MessageType
{
INFO,
WARNING,
ERROR,
FATAL
}
public class Message
{
[JsonConverter(typeof(StringEnumConverter))]
public MessageType MessageType { get; set; }
public bool MessageTypeSpecified;
[JsonConverter(typeof(StringEnumConverter))]
public MessageCode MessageCode { get; set; }
public bool MessageCodeSpecified;
public string MessageParameters;
public string MessageText;
}
While getting the response for the object (Message) using postman the response was as below
"messages": [
{
"messageTypeSpecified": false,
"messageCodeSpecified": false,
"messageParameters": null,
"messageText": "0"
}
]
I was not able to get the enum values in response.
so tried the below options
Decorate the Class Property - https://exceptionnotfound.net/serializing-enumerations-in-asp-net-web-api/
Decorate the Enumeration - https://exceptionnotfound.net/serializing-enumerations-in-asp-net-web-api/
Add the Converter Globally - https://exceptionnotfound.net/serializing-enumerations-in-asp-net-web-api/
Mentioning enum member ([EnumMember]) in each enum values.
Nothing worked out.
You accidentally hit a Newtonsoft feature (not very well documented). A longer description can be found in this
question.
In short: you have a property named MyPropertyName and one named MyPropertyNameSpecified,i.e Specified appended to other property name, the default behaviour for Newtonsoft is to not serialize MyPropertyName when MyPropertyNameSpecified is false.
The solution to your problem would be either to rename some of the properties or use these settings:
new JsonSerializerSettings
{
ContractResolver = new DefaultContractResolver { IgnoreIsSpecifiedMembers = true }
};
To edit JSON serializer settings in a .NET Core project supply the options in your ConfigureServies method:
services.AddMvc()
.AddJsonOptions(
options =>
{
options.SerializerSettings.ContractResolver =
new DefaultContractResolver { IgnoreIsSpecifiedMembers = true };
});

Configuration.GetSection() easily gets primitive string values but not complex values

This really amazes me. I am reading values from appsettings.json using Configuration.GetSection method and in nutshell my appsettings.json looks like below:
"AppSettings":
{
"PathPrefix": "",
"Something": "Something else",
"Clients":{"foo": "bar"}
}
Now I surprises me is that if I do something like below:
var foo = Configuration.GetSection("AppSettings:Clients:foo").Value;
Then it gets the value correctly. It gets the value bar
However, when I do
var clients = Configuration.GetSection("AppSettings:Clients").Value;
it returns null. It's not only this field, whenever I call getSection method to get any complex object then it returns null but when I call it to get a basic string value then it gets the value correctly even though seeminglyi, it had problems in getting its parent element. This baffles me and raises three questions:
Why would it have issues getting complex values but not getting basic string values?
Is it by design? If so , why?
If I want to load entire object, how do I do that?
You can load an entire object using a strongly typed object.
First, create a class (or classes) to hold you settings. Based on your example this would look like:
public class AppSettings
{
public string PathPrefix { get; set; }
public string Something { get; set; }
public Clients Clients { get; set; }
}
public class Clients
{
public string foo { get; set; }
}
Now, you need to add the Options service to your service collection and load your settings from the configuration:
public void ConfigureServices(IServiceCollection services)
{
// This is only required for .NET Core 2.0
services.AddOptions();
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddMvc();
}
You now access the properties by injecting them into your class, for example:
public class HomeController : Controller
{
private readonly AppSettings _settings;
public HomeController(IOptions<AppSettings> settings)
{
_settings = settings.Value;
}
}
You can also load suboptions in the ConfigureService method by specifying the configuration section to load e.g.
services.Configure<Clients>(Configuration.GetSection("AppSettings:Clients");
Now you can inject IOptions<Clients> to access those settings
The official documentation can be found here
What would you expect it to return? You can get complex objects using the Get<T> extension method. Try this:
var clients = Configuration.GetSection("AppSettings:Clients").Get<YourClientsType>();

Configuration.GetSection in Asp.Net Core 2.0 getting all settings

I am trying to learn the various ways to retrieve configuration info so I can determine the best path for setting up and using configuration for an upcoming project.
I can access the various single settings using
var sm = new SmsSettings
{
FromPhone = Configuration.GetValue<string>("SmsSettings:FromPhone"),
StartMessagePart = Configuration.GetValue<string>("SmsSettings:StartMessagePart"),
EndMessagePart = Configuration.GetValue<string>("SmsSettings:EndMessagePart")
};
I also need to be able to count settings, determine values of certain settings etc. So I was building a parsing method to do these types of things and needed a whole section of the settings file, which is what I assumed GetSection did.
Wrong.
appsettings.json :
{
"ConnectionStrings": {
"DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=TestingConfigurationNetCoreTwo;Trusted_Connection=True;MultipleActiveResultSets=true",
"ProductionConnection": "Server=(localdb)\\mssqllocaldb;Database=TestingConfigurationNetCoreTwo_Production;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Logging": {
"IncludeScopes": false,
"LogLevel": {
"Default": "Warning"
}
},
"SmsSettings": {
"FromPhone": "9145670987",
"StartMessagePart": "Dear user, You have requested info from us on starting",
"EndMessagePart": "Thank you."
}
}
This code:
var section = Configuration.GetSection("ConnectionStrings");
Returns these results:
A few questions arise.
Why is this returning 3 different JsonConfigurationProviders, one of which includes every setting in the appsettings.json file (shown in Image 2)
Why isn't GetSection("ConnectionStrings") actuall doing just that, returning the sub children of the ConnectionStrings
Given number 2, how do you actually just retrieve the children of ConnectionStrings ?
Assuming a model ConnectionStrings, with one property, List Connections, can the section be converted to an object?
I understand the answer has been accepted. However, providing proper example code, just in case anyone looking to understand a bit more...
It is quite straight forward to bind custom strong type configuration. ie. configuration json looks like below
{
"AppSettings": {
"v": true,
"SmsSettings": {
"FromPhone": "9145670987",
"StartMessagePart": "Dear user, You have requested info from us on starting",
"EndMessagePart": "Thank you."
},
"Auth2Keys": {
"Google": {
"ClientId": "",
"ClientSecret": ""
},
"Microsoft": {
"ClientId": "",
"ClientSecret": ""
},
"JWT": {
"SecretKey": "",
"Issuer": ""
}
}
}
}
and your C# classes looks like
public class SmsSettings{
public string FromPhone { get; set;}
public string StartMessagePart { get; set;}
public string EndMessagePart { get; set;}
}
public class ClientSecretKeys
{
public string ClientId { get; set; }
public string ClientSecret { get; set; }
}
public class JWTKeys
{
public string SecretKey { get; set; }
public string Issuer { get; set; }
}
public class Auth2Keys
{
public ClientSecretKeys Google { get; set; }
public ClientSecretKeys Microsoft { get; set; }
public JWTKeys JWT { get; set; }
}
You can get the section by GetSection("sectionNameWithPath") and then Convert to strong type by calling Get<T>();
var smsSettings = Configuration.GetSection("AppSettings:SmsSettings").Get<SmsSettings>();
var auth2Keys= Configuration.GetSection("AppSettings:Auth2Keys").Get<Auth2Keys>();
For simple string values
var isDebugMode = Configuration.GetValue("AppSettings:IsDebugMode");
Hope this helps...
according to this post
https://github.com/aspnet/Configuration/issues/716
the GetSection("Name").Value will return null, you must use GetChildren to get the child items
Bind will populate the properties aginst the provided object, by default it maps against public properties, look at the update to support private properties.
try Get<T>() over bind, it will provide you a strongly typed instance of the configuration object
try a simple POCO of your class (no complex getter/setters, all public, no methods) and then take it from there
Update:
From .net core 2.1 BindNonPublicProperties added to BinderOptions, so if set to true (default is false) the binder will attempt to set all non read-only properties.
var yourPoco = new PocoClass();
Configuration.GetSection("SectionName").Bind(yourPoco, c => c.BindNonPublicProperties = true)
If you use GetSections() along with Bind() you should be able to create poco objects for your use.
var poco= new PocoClass();
Configuration.GetSection("SmsSettings").Bind(poco);
This should return to you a poco object with all the values set.
If you use the Bind method on the object returned by GetSection, then this would bind the key value pairs within the section to corresponding properties of the object it has been bound too.
For example,
class ConnectionStrings {
public string DefaultConnection { get; set;}
public string ProductionConnection {get; set;}
}
..
var connectionStrings = new ConnectionStrings();
var section = Configuration.GetSection("ConnectionStrings").Bind(connectionStrings);
It works for me on .Net Core directly on Razor HTML:
#Html.Raw(Configuration.GetSection("ConnectionStrings")["DefaultConnectoin"]) <!-- 2 levels -->
#Html.Raw(Configuration.GetSection("Logging")["LogLevel:Default"]) <!-- 3 levels -->
#Html.Raw(Configuration.GetSection("SmsSettings")["EndMessagePart"]) <!-- 2 levels -->
Reference:
https://www.red-gate.com/simple-talk/dotnet/net-development/asp-net-core-3-0-configuration-factsheet/
If you required any section with "GetSection" and (key,value), try this:
Configuration.GetSection("sectionName").GetChildren().ToList()
and get a Collection of keys with vallues, can manipulate with LinQ

Categories

Resources