I have app settings that look like this:
"Tenants": [
{
"Id": "00000000-0000-0000-0000-00000000000",
"ConnectionString": "dbstring"
},
{
"Id": "00000000-0000-0000-0000-00000000001",
"ConnectionString": "dbstring"
}
]
and an object that looks like this:
public class TenantSecrets
{
public string ConnectionString { get; set; }
public Guid Id { get; set; }
}
public class Tenants : List<TenantSecrets> { }
When I try to either configure them or bind them like so:
services.Configure<Tenants>(Configuration.GetSection("Tenants"));
var tenants = new Tenants();
Configuration.Bind("Tenants", tenants);
The list is always empty, does anyone know why or how I can debug it? I can see the list in the appsettings configuration reader when I debug it, but the object never seems to map.
ok people, i figured it out, turns out that the empty guid i was using for the Id was one 0 short of a guid, and i have now learnt that config to poco mapping fails silently, so for anyone else having issues, please check that your data fits the data types
Edit:
Good, you spotted this mistake, it happens it works perfectly. I don't want to write a useless answer so here is the version using the Options pattern.
Configure the Tenants class in the Startup.cs file (method ConfigureServices)
services.Configure<Tenants>(Configuration.GetSection("Tenants"));
Then, inject the IOptions<Tenants> service in a controller constructor.
private readonly Tenants _tenants;
public HomeController(IOptions<Tenants> tenantOptions)
{
_tenants = tenantOptions.Value;
}
Related
I have a C# function app written in .NET 6.0 and I want to read a collection of settings from the local.settings.json file. Here is my startup file that I want to read my settings so I can access them later in my application:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
BindAndRegisterStronglyTypedConfig(builder);
}
private void BindAndRegisterStronglyTypedConfig(IFunctionsHostBuilder builder)
{
var products = new Products();
builder.GetContext().Configuration.Bind(nameof(Products), products);
builder.Services.AddSingleton(products);
}
}
Then in my local.settings.json file I have the products that I want to read:
{
"Values": {
"Products": [
{
"ProductId": "#",
"Title": "#"
},
{
"ProductId": "#",
"Title": "#"
}
]
}
}
The models the values should bind to:
public class Products
{
public readonly List<Product> products = new List<Product>();
}
public class Product
{
public string ProductId { get; set; }
public string Title { get; set; }
}
Anybody understand what I've done wrong, I get no error just a null value for collection?
Check if the below findings help the requirement:
read my local settings so I can access them later in my application:
One of the ways is in the Same Function Trigger Code, you can fetch the Configuration settings using Configuration class. Below is the sample code for it, taken from the SO reference 70576009
public static object GetAppSettings(IConfiguration configuration, ILogger log)
{
string localsetting1 = configuration.GetValue("Setting1");
}
And another workaround is to Check the local.settings.json configuration code should be in clear format with the Parent-child format where child will be in key-value relationship which is similar to your given configuration and that configuration settings fetching process code and the format of local.settings.json is given in the SO#72786915 by the user #SimonPrice.
Your local.settings.json code is same given in the above reference if it is formatted such as:
{
"Values": {
"Products:ProductId": "#",
"Products:Title": "#",
"Products:ProductId2": "#",
"Products:Title2": "#",
}
}
So I have a simple configuration class PubsubSettings:
public class PubSubSettings
{
public string ProjectId { get; set; }
public string TopicId { get; set; }
public int PartnerId { get; set; }
public string SubscriptionId { get; set; }
}
I have previously only had one of these configured in my appsettings.json but now I want to be able to handle an arbitrary number of them.
I have another class, PubSub, that I usually inject an IOptions<PubSubSettings> into. And this, in turn, gets injected into my Worker class.
services.Configure<PubSubSettings>(configuration.GetSection(nameof(PubSubSettings)));
...
services.AddHostedService<Worker>();
So, what I want to do now, is add a new Worker as a hosted service for each entry in my AppSettings PubSubSettings section and inject the relevant IOptions<PubSubSettings> into each of these (along with the standard ILogger).
So in essence, I'd like this config block:
"PubsubSettings": [
{
"ProjectId": "project1",
"TopicId": "topic",
"PartnerId": 1,
"SubscriptionId": "sub1"
},
{
"ProjectId": "project2",
"TopicId": "topic2",
"PartnerId": 2,
"SubscriptionId": "sub2"
}
]
To end up with two hosted services being created, one with the first set of options and the other with the second.
I've seen a few questions looking for similar things but nothing I could find quite lined up with this so I'm a bit stumped. Any ideas?
The solution is Dotnet 5.
So from what I've been able to find, there's no way to do this out-of-the box.
However, This can be done manually using a combination of ActivatorUtilities and Configuration.Bind().
private void CreateWorkers(IServiceCollection services, IConfigurationRoot configuration)
{
List<PubSubSettings> pubsubSettings = new();
configuration.Bind(nameof(PubSubSettings), pubsubSettings);
foreach (PubSubSettings setting in pubsubSettings)
{
services.AddSingleton<IHostedService>(s => ActivatorUtilities.CreateInstance<Worker>(s, ActivatorUtilities.CreateInstance<PubSub.PubSub>(s, setting)));
}
}
Essentially, you can use Bind to get the configuration objects from the JSON. Then you can manually construct the Worker for the call to AddHostedService using CreateInstance.
Two calls are needed in this case, one to generate the PubSub for the worker (in which we pass the setting parameter) and the other to generate the Worker itself.
ActivatorUtilities essentially injects everything you need for the object except the parameters you've provided.
We need to use .AddSingleton<IHostedService> because of the way that the framework checks for dupes with AddHostedService().
Maybe you could try creating a class only for the object and let the PubSubSettings class only for the array:
public class PubSubSettings
{
public PubSubObject[] PubSubs { get; set; }
}
public class PubSubObject
{
public string ProjectId { get; set; }
public string TopicId { get; set; }
public int PartnerId { get; set; }
public string SubscriptionId { get; set; }
}
Then in the startup class you should use Bind to get the current value of the array to create a Worker for each PubSub:
PubSubSettings settings = new PubSubSettings();
Configuration.GetSection(nameof(PubSubSettings)).Bind(settings);
...
foreach(PubSubObject item in settings.PubSubs)
{
services.AddHostedService<Worker>();
}
Then in the PubSub class you need to search the PartnerId inside the Array.
Or you could follow the approach described in the section Named options support using IConfigureNamedOptions in the Microsoft docs: Options pattern in ASP.NET Core
I am using standard configuration pattern for ASP.NET Core applications and I can not bind configuration to my class as it has construtor with parameters.
In appsettings.json I included desired config:
"MyServiceConfig": {
"Identity": {
"Version": "1.0",
"ComplicatedUri": {
"Scheme": "http",
"Authority": "localhost",
"Path": "SuperService"
}
}
},
My config class and it's dependencies look like that:
public class MyServiceConfig
{
public MyIdentity Identity { get; set; }
}
public class MyIdentity
{
public string IdentityName { get; set; }
public string Version { get; set; }
public MyComplicatedUri ComplicatedProperty { get; set; }
public MyIdentity(string version, MyComplicatedUri complicatedProperty)
{
Version = version;
ComplicatedProperty = complicatedProperty;
IdentityName = complicatedProperty.Path;
}
}
public class MyComplicatedUri
{
public string Scheme { get; set; }
public string Authority { get; set; }
public string Path { get; set; }
}
I have already tried code like that:
private MyServiceConfig GetMyConfig(IConfiguration configuration)
{
var config = new MyServiceConfig();
configuration.GetSection("MyServiceConfig").Bind(config);
return config;
}
It throws exception:
'Cannot create instance of type 'MyIdentity' because it is missing
a public parameterless constructor.'
That behaviour can make sense in some cases but in that particular one not so much. Mappings could be straightforward - by property names which have public setters or by constructor parameter names.
Another idea would be adding converter in AddJsonOptions in Startup class for my types - IConfiguration.Bind could infer how to construct it but I also tried that with no success.
Have you encoutered similar problems and found some reasonable solution to that?
Edit: Adding parameterless constructor will work of course, but sometimes I need to deal with some classes from external packages I'd like to use as parts of my config class so let's assume we can not modify them. I'd like to avoid adding new types for mapping only as well. Ideally I'd like to force ASP.NET Core engine to use existing constructor with parameters and by parameter name map with json properties - which currently is not working.
You should just add a default constructor in MyIdentity class.
.bind() binds the configuration into the object using the default constructor.
So, add the required default constructor in your MyIdentity class and it will be fine.
public MyIdentity(){}
Also, you can use Options.
In ConfigureServices, add the following:
services.AddOptions();
services.ConfigureOptions<MyServiceConfig>();
and then use dependency injection to initialize it.
In addition, use your own JsonConverter
I couln't find a similar case here, hence my question. I have a json like this:
{
"prop1": "bla",
"propn": "bla",
"Data": {
"42": {
"prop1": "bla",
"prop2": "bla",
"Symbol": "42"
},
"abc": {
"prop1": "bla",
"prop2": "bla",
"Symbol": "abc"
}
},
"Type": 100
}
Now, how do I get all elements from Data, and the most I am interested in the ones that have the symbol property set. I tried Newtonsoft.json.linq and jobject, but got really no clue what to do here. Any guidance anyone? Thanks!
Ronald
What you're looking for is called 'deserialize'. You have a string (the json in you post) and you want to turn it into an object.
The first steps you need to do are:
Create a class that matches your data.
Simply copy your json string in your post and use the option in Visual Studio to 'paste JSON as class'. Perhaps clean it up by changing the name RootObject to something more descriptive.
Install the NuGet package Newtonsoft in Visual Studio.
Now you can use MyClass myObject = JsonConvert.DeserializeObject<MyClass>(myString);
To access Symboljust use myObject.Data.Symbol
I imagine that once you extract partial data from json, if you still need to pass the data through your application, a dedicated model will come handy.
public class Data
{
public Element abc { get; set; }
}
public class Element
{
public string prop1 { get; set; }
public string prop2 { get; set; }
public string Symbol { get; set; }
}
While you certainly can rely on JObject handling the deserialization, i find it more intuitive to work with anonymous templates, especially for partial data retrieval.
var template = new
{
Data = default(Data)
};
var instance = JsonConvert.DeserializeAnonymousType(json, template);
will give you something like
I recomend you to use Jil library, is faster and more simple than Newtonsoft.json
I am interested in adding support for partial updates in my ASP.NET Core WebAPI where I only update the properties on a resource that the caller provided, leaving excluded properties unchanged.
For context, imagine I have a resource that can be described as follows:
GET /users/1
{
title: "Mister",
firstName: "Frederick",
middleName: "McFeely",
lastName: "Rodgers"
}
If I wanted to allow consumers to change the value stored in the firstName property from "Frederick" to "Fred" in isolation, I should be able to expose a PATCH endpoint that supports the JSON Merge Patch Content-Type, like so:
PATCH /users/1
Content-Type: application/merge-patch+json
{
firstName: "Fred"
}
However, I see no easy way for me to know that firstName is the only property being updated. For example, if I were to make a controller that accepted PATCH verbs, it could be scaffolded like this:
[Route("users")]
public class UsersController : Controller {
[HttpPatch("{userId:int}")]
public User Patch([FromRoute] int userId, [FromBody] User user) {
// How do I know which properties were set on User at this point?
}
}
public class User {
public String Title { get; set; }
public String FirstName { get; set; }
public String MiddleName { get; set; }
public String LastName { get; set; }
}
But I don't see how I can extract which properties' had keys defined on the JSON object before it was hydrated as a User and passed to my controller. I cannot assume a value of null to mean a property was excluded as the caller could be explicitly setting an optional property to null.
Edit
I am aware of the Microsoft.AspNetCore.JsonPatch library. This, unfortunately, expects the caller to use the "[description of changes]" to define a PATCH as described in RFC 5789, which I find unintuitive and verbose. I am referring to the "JSON Merge Patch" defined in RFC 7396.
I found a library that works: https://github.com/Morcatko/Morcatko.AspNetCore.JsonMergePatch
[HttpPatch]
[Consumes(JsonMergePatchDocument.ContentType)]
public void Patch([FromBody] JsonMergePatchDocument<Model> patch)
{
...
patch.ApplyTo(backendModel);
...
}
Or use patch.JsonPatchDocument.Operations to walk through patch request fields manually.
for simple types, I found a very simple solution using Newtonsoft.Json merge of JObjects:
public static T Patched<T>(T source, JObject patch) where T : class
{
var sourceObject = JObject.FromObject(source);
sourceObject.Merge(patch, new JsonMergeSettings() {MergeArrayHandling = MergeArrayHandling.Union});
return sourceObject.ToObject<T>();
}
public static T Patched<T>(T source, string patchCode) where T : class
{
return Patched<T>(source, JObject.Parse(patchCode));
}
Hope this helps someone searching for this topic and looking for a simple solution without external packages.
It appears like, for merge patch you will have to wait for odata support.
It is in beta at the moment and supports the merge semantics with the Delta<> class.
https://www.nuget.org/packages/Microsoft.AspNetCore.OData/
For doing a patch, you have to define PatchDocument.
More about it you can find PatchDocument
Example of method.
[HttpPatch("{userId:int}")]
public IActionResult UserPatch(int userId, [FromBody] JsonPatchDocument<User> patchDocument) {
var user = new User();
// Because it comes from url.
user.Id = userId;
patchDocument.ApplyTo(user);
// Here you call context or repository to save.
}
Example of document.
[
{ "op": "replace", "path": "/firstName", "value": "boo" },
]
That will update firstName field to 'boo' in user model.
What you might be looking for is ASP.Net Core JsonPatchDocument
https://github.com/aspnet/JsonPatch
https://learn.microsoft.com/en-us/aspnet/core/api/microsoft.aspnetcore.jsonpatch