Using MVC .net Core and building a concrete config class within the startup class. My appsettings.json looks like this:
{
"myconfig": {
"other2": "tester,
"other": "tester",
"root": {
"inner": {
"someprop": "TEST VALUE"
}
}
}
}
I've represented this with a concrete class as follows:
public class TestConfig
{
public string other2 { get; set; }
public string other { get; set; }
public Inner1 root { get; set; }
}
public class Inner1
{
public Inner2 inner { get; set; }
}
public class Inner2
{
public string someprop { get; set; }
}
And I can easily map this by doing the follow:
var testConfig = config.GetSection("myconfig").Get<TestConfig>();
However.... what I don't like about the above is the need to make TestConfig more complex than it needs to be. Ideally, I'd like something like this:
public class PreciseConfig
{
[Attribute("root:inner:someprop")]
public string someprop { get; set; }
public string other { get; set; }
public string other2 { get; set; }
}
Where I don't have to have the nested objects within and can map directly to a lower property in this kind of way. Is this possible? Using .net Core 2.1.
Thanks for any pointers in advance!
P.s. I know I can create an instance of PreciseConfig myself and set properties using config.GetValue<string>("root:inner:someprop") BUT I don't want to have to set all my custom settings in this way if I can do them automatically using a serialization property or similar.
For the higher level config you get the configuration as normal with the top node.
Then use the path myconfig:root:inner to get the other desired section and Bind PreciseConfig from the previous step
var preciseConfig = config.GetSection("myconfig").Get<PreciseConfig>();
config.GetSection("myconfig:root:inner").Bind(preciseConfig);
Reference Configuration in ASP.NET Core : GetSection
Reference Configuration in ASP.NET Core : Bind to an object graph
Related
.net6 - I have this working fine
{
"SomeSetting": {
"A": "value1",
"B": "value2"
}
}
internal class MyClass // <-- NOTE, the class is internal
{
public string A { get; set; } // <-- but property must be public, including public setter
public string B { get; set; }
}
. . . . .
var inst = config.GetSection("SomeSetting").Get<MyClass>();
That works fine ^^.
But it is not binding unless property has name or access level - public. Is it possible to have something like this? --
internal class MyClass
{
internal string A { get; set; } // <-- internal
internal string B { get; set; }
}
or
internal class MyClass
{
public string A { get; internal set; } // <-- internal setter
public string B { get; internal set; }
}
or
internal class MyClass
{
[SomeAttribute()]
internal string A { get; set; } // <-- internal
// or
[SomeOtherAttribute("B")]
internal string BBB { get; set; } // <-- property name is not matching json file
}
I did not look deep into DLLs, I thought that this is JSON binding, so I tried
using System.Text.Json.Serialization; JsonPropertyNameAttribute JsonIncludeAttribute
But so far - no luck
I found the way. The immutability with System.Text.Json as offered by #KirkWoll (thank you - good learning) did not work. I guess, I can't guarantee that .Get<MyClass>() uses that technology. I tried every way described in the document without success.
But there was a simple way, right there. You just create your read-only model like this
public class MyClass
{
public string A { get; private set; }
public string B { get; private set; }
}
and bind it like this - use the additional options
var inst = config.GetSection("SomeSetting")
.Get<MyClass>(options => { options.BindNonPublicProperties = true; });
This solves the issue of Read-Only model bind to JSON in IConfiguration
I have an API that returns a JSON string and I want to parse that JSON string into an object. I tried creating the object but with no luck. Below is the sample JSON string that I want to get the value from. Any idea as to what the class looks like in order to parse that JSON object into an object? My main concern is to get the code which is "platinum" under currentCard.
{
"status" : {
"currentCard" : {
"code" : "platinum"
},
"status" : {
"index" : 0,
"value" : "This is a sample text."
}
}
}
You need to use a website such as https://json2csharp.com/ or use the inbuilt tools in VS Studio (Edit->Paste Special->Paste JSON as classes) which will automatically create classes from JSON.
The above mentioned website suggests the following:
public class CurrentCard
{
public string code { get; set; }
}
public class Status2
{
public int index { get; set; }
public string value { get; set; }
public CurrentCard currentCard { get; set; }
public Status status { get; set; }
}
public class Root
{
public Status status { get; set; }
}
Which you can then deserialise like this:
Root myDeserializedClass = JsonConvert.DeserializeObject<Root>(myJsonResponse);
This seems like it should be really simple, I have been searching SO and a lot of other places for an answer to this, everything I have found and tried does not work.
I have an appsettings.json file that looks like this
"Email": {
"Port": "25",
"Host": "localhost",
"EnableSSL": "false",
"Credentials": {
"Username": "fakeuser",
"Password": "fakepassword"
},
"SystemFromAddress": "testsender#localhost.com",
"SystemFromDisplayName": "Test Sender",
"EmailTemplateRootDirectory": "Email\\EmailTemplates",
"EmailTemplates": [
{
"TemplateKey": "ResetPassword",
"TemplatePath": "ResetPassword.cshtml"
},
{
"TemplateKey": "NewAccount",
"TemplatePath": "NewAccount.cshtml"
},
{
"TemplateKey": "VerifyEmail",
"TemplatePath": "VerifyEmail.cshtml"
}
]
}
There are several models (EmailOptions being the parent) that I am trying to bind to, the EmailOptions class is expecting to have it's EmailTemplates list populated from the EmailTemplates list in the appsettings.json as seen above.
The parent class is being populated by the appsettings.json file as expected, the Child List of Email Templates in this class is always coming up empty.
Here are the classes I am binding to.
public class EmailOptions
{
public int Port { get; set; }
public string Host { get; set; }
public bool EnableSSL { get; set; }
public EmailCredentials Credentials { get; set; }
public string SystemFromAddress { get; set; }
public string SystemFromDisplayName { get; set; }
public string EmailTemplateRootDirectory { get; set; }
public IEnumerable<EmailTemplate> EmailTemplates { get; set; } = new List<EmailTemplate>();
}
public class EmailTemplate
{
public string TemplateKey { get; set; }
public string TemplatePath { get; set; }
}
public class EmailCredentials
{
public string Username { get; set; }
public string Password { get; set; }
}
I am using the following call I am making in my startup class in ASP.NET Core.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddOptions();
services.Configure<EmailOptions>( _configuration.GetSection("Email" ));
...
For some reason the IEnumerable property in my EmailOptions is not being deserialized from the appsettings.json into my options - when I attempt to use it anywhere in my controllers - the list is always set to an empty array.
FWIW: I have this working in a console application where I have more control over setting up my options from the appsettings.json. Here is what I am doing in the console app, (I am leaving out the code where I set up the options with the DI container for brevity)
var emailSection = configuration.GetSection( "Email" );
var emailOptions = emailSection.Get<EmailOptions>();
emailOptions.EmailTemplates = configuration.GetSection( "Email:EmailTemplates" ).Get<List<EmailTemplate>>();
as expected - in the console application, I get my Email Templates because i have the ability to get the child list separately and add it to the options before handing it over to the DI container. I don't seem to have that flexibility in the ASP.NET Core IServiceCollection.Configure() extension method (so maybe use another method to do this? which one? After a couple hours of searching I am crying uncle and asking for help).
So how does one get this to work using the ASP.NET Core "IServiceCollection.Configure()" method? Is there a better way to do this?
Thank you Joe for pointing out what needed to happen!
I made the false assumption that the serializer would happily create it's list from the json and assign that list to my IEnumerable - rather - you need to make sure to use List if you intend to deserialize a list of json objects into your Options (and other concrete dotnet types where applicable).
so instead of this
IEnumerable<EmailTemplate> EmailTemplates { get; set; }
I should have had this...
List<EmailTemplate> EmailTemplates { get; set; }
I'm trying to use AutoMapper to take data from a class that has prefixes before property names and map it to a second class that doesn't have those prefixes. However, I don't necessarily want it to always strip out that prefix: I just want it to do it for this particular mapping.
My source class looks like this:
public class AdvancedSearchFilterDataModel
{
// ....
public string ServiceMeterNumber { get; set; }
// ....
}
My destination class looks like this:
[DataContract]
public class ServicesAdvancedSearchFilterData : AdvancedSearchFilterData
{
// ....
[DataMember]
public string MeterNumber { get; set; }
// ....
}
When I try to map values like this, it works:
Mapper.Configuration.RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>();
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
But I only want "Service" to be recognized as a prefix for certain mappings, since it's also used as a normal part of property names in other mappings. I tried to handle this with a profile, but this didn't work -- no data was mapped:
Mapper.CreateProfile("ServicePrefix").RecognizePrefixes("Service");
Mapper.CreateMap<AdvancedSearchFilterDataModel, ServicesAdvancedSearchFilterData>()
.WithProfile("ServicePrefix");
ServicesAdvancedSearchFilterData servciesFilterData =
Mapper.Map<ServicesAdvancedSearchFilterData>(model);
How can I make it recognize the prefix only when I want it to, either using profiles or some other technique? (I also have other prefixes that I'm going to need it to recognize for other mappings in the same way.)
I achieved this functionality by creating following structure:
I have Person model for my view which is flattened from PersonCombined
public class PersonCombined
{
public Person Person { get; set; }
public Address DefaultAddress { get; set; }
public Contact EmailContact { get; set; }
public Contact PhoneContact { get; set; }
public Contact WebsiteContact { get; set; }
}
public class Person : IWebServiceModel
{
public int ID { get; set; }
public string PersonFirstName { get; set; }
public string PersonSurname { get; set; }
public string PersonDescription { get; set; }
public Nullable<bool> PersonIsActive { get; set; }
}
Then I have separate class for this mapping only that looks like this:
public class PersonCustomMapping : ICustomMapping
{
const string separator = " ";
private static IMappingEngine _MappingEngine;
public IMappingEngine MappingEngine
{
get
{
if (_MappingEngine == null)
{
var configuration = new ConfigurationStore(new TypeMapFactory(), AutoMapper.Mappers.MapperRegistry.Mappers);
configuration.RecognizePrefixes("Person");
configuration.RecognizeDestinationPrefixes("Person");
configuration.CreateMap<Person, MCIACRM.Model.Combine.PersonCombined>();
configuration.CreateMap<MCIACRM.Model.Combine.PersonCombined, Person>();
_MappingEngine = new MappingEngine(configuration);
}
return _MappingEngine;
}
}
}
In my generic view I have mappingEngine property like this:
private IMappingEngine mappingEngine
{
get
{
if (_mappingEngine == null)
{
_mappingEngine = AutoMapper.Mapper.Engine;
}
return _mappingEngine;
}
}
Finally in my generic view constructor i have:
public GenericEntityController(IGenericLogic<S> logic, ICustomMapping customMapping)
: base()
{
this._mappingEngine = customMapping.MappingEngine;
this.logic = logic;
}
And that's how I do mapping:
result = items.Project(mappingEngine).To<R>();
or
logic.Update(mappingEngine.Map<S>(wsItem));
Because I use 1 entity per view I can define custom mapping configuration per entity.
Hope this helps
Let's say I have a class from a 3rd-party, which is a data-model. It has perhaps 100 properties (some with public setters and getters, others with public getters but private setters). Let's call this class ContosoEmployeeModel
I want to facade this class with an interface (INavigationItem, which has Name and DBID properties) to allow it to be used in my application (it's a PowerShell provider, but that's not important right now). However, it also needs to be usable as a ContosoEmployeeModel.
My initial implementation looked like this:
public class ContosoEmployeeModel
{
// Note this class is not under my control. I'm supplied
// an instance of it that I have to work with.
public DateTime EmployeeDateOfBirth { get; set; }
// and 99 other properties.
}
public class FacadedEmployeeModel : ContosoEmployeeModel, INavigationItem
{
private ContosoEmployeeModel model;
public FacadedEmployeeModel(ContosoEmployeeModel model)
{
this.model = model;
}
// INavigationItem properties
string INavigationItem.Name { get; set;}
int INavigationItem.DBID { get; set;}
// ContosoEmployeeModel properties
public DateTime EmployeeDateOfBirth
{
get { return this.model.EmployeeDateOfBirth; }
set { this.model.EmployeeDateOfBirth = value; }
}
// And now write 99 more properties that look like this :-(
}
However, it's clear that this will involve writing a huge amount of boilerplate code to expose all the properties , and I'd rather avoid this if I can. I can T4 code-generate this code in a partial class, and will do if there aren't any better ideas, but I though I'd ask here to see if anyone had any better ideas using some super wizzy bit of C# magic
Please note - the API I use to obtain the ContosoEmployeeModel can only return a ContosoEmployeeModel - I can't extend it to return a FacededEmployeeModel, so wrapping the model is the only solution I can think of - I'm happy to be corrected though :)
The other approach may be suitable for you is to use AutoMapper to map base class to your facade here is sample code:
class Program
{
static void Main(string[] args)
{
var model = new Model { Count = 123, Date = DateTime.Now, Name = "Some name" };
Mapper.CreateMap<Model, FacadeForModel>();
var mappedObject = AutoMapper.Mapper.Map<FacadeForModel>(model);
Console.WriteLine(mappedObject);
Console.ReadLine();
}
class Model
{
public string Name { get; set; }
public DateTime Date { get; set; }
public int Count { get; set; }
}
interface INavigationItem
{
int Id { get; set; }
string OtherProp { get; set; }
}
class FacadeForModel : Model, INavigationItem
{
public int Id { get; set; }
public string OtherProp { get; set; }
}
}
Resharper allows the creation of "delegating members", which copies the interface of a contained object onto the containing object and tunnels the method calls/property access through to the contained object.
http://www.jetbrains.com/resharper/webhelp/Code_Generation__Delegating_Members.html
Once you've done that, you can then extract an interface on your proxy class.