How to get section from a configuration file - c#

I want to transfer data from appsettings.json to an instance of MailSettings at runtime :
Here's the model :
public class MailSettings
{
public string Mail { get; set; }
public string DisplayName { get; set; }
public string Password { get; set; }
public string Host { get; set; }
public int Port { get; set; }
}
In program.cs, I try to configure the service with the following instruction:
builder.Services.Configure<MailSettings>(Configuration.GetSection("MailSettings"));
But I have the following problem:
Compiler Error CS0120 : An object reference is required for the
nonstatic field, method, or property
Configuration.GetSection(string)
If someone has a solution ...

try this.
builder.Services.Configure<MailSettings>(builder.Configuration.GetSection("MailSettings"));

1. If you want to get section in program.cs
Try
var settings = builder.Configuration.GetSection("MailSettings").Get<MailSettings>();
Read this answer to know more
Result:
in appsetings.json:
"MailSettings": {
"Mail": "a#a.com",
"DisplayName": "aa",
"Password": "123"
}
2. If you want to get section in Controller/class
Try
builder.Services.Configure<MailSettings>(builder.Configuration.GetSection("MailSettings"));
HomeController:
public class HomeController : Controller
{
public MailSettings MailSettings { get; }
public HomeController(IOptions<MailSettings> smtpConfig)
{
MailSettings = smtpConfig.Value;
}
}
Result:

Related

.NET Core API REST C# List into List is null

I'm developing an api in net core.
I've done a post function in which I send an object containing multiple parameters and a list within another list.
When I'm debugging the code the function is called correctly but I find that the second list always arrives null.
The rest of the data arrives at you correctly. I have done different tests with other objects and everything works correctly.
It is this case in which the list within another the second one arrives null.
My code:
example request input
{
"Name": "TestName",
"Related1":
[{
"id1": "TestNameRelated1",
"Related2":
[{
"id2": "TestNameRelated2"
}]
}]
}
[HttpPost]
public resultExample Test([FromBody]TestClass test)
{
//do something
}
[DataContract]
public class TestClass
{
[DataMember]
public string Name { get; set; }
[DataMember]
public List<TestClassArray> Related1 { get; set; }
}
[DataContract]
public class TestClassArray
{
[DataMember]
public string id1 { get; set; }
[DataMember]
public List<TestClassArray2> Related2 { get; set; }
}
[DataContract]
public class TestClassArray2
{
[DataMember]
public string id2 { get; set; }
}
This api was previously made in .NET framework 4.8 and this case worked correctly.
Now I'm passing the api to .Net5.
Could it be that in .Net5 it is not allowed to pass lists within other lists?
Do you have to enable some kind of configuration to be able to do this now?
You need use class/DTO with constructor like shown below and you should be good to go. I have uploaded this sample API app's code working with .net5.0 on my GitHub here.
public class TestClass
{
public TestClass()
{
Related1 = new List<TestClassArray>();
}
public string Name { get; set; }
public List<TestClassArray> Related1 { get; set; }
}
public class TestClassArray
{
public TestClassArray()
{
Related2 = new List<TestClassArray2>();
}
public string id1 { get; set; }
public List<TestClassArray2> Related2 { get; set; }
}
public class TestClassArray2
{
public string id2 { get; set; }
}
public class ResultExample
{
public string StatusCode { get; set; }
public string Message { get; set; }
}
Controller Post Method
[HttpPost]
[ProducesResponseType(typeof(ResultExample), 200)]
public ResultExample Post([FromBody] TestClass test)
{
ResultExample testResult = new ResultExample();
TestClass test2 = new TestClass();
TestClassArray testClassArray = new TestClassArray();
TestClassArray2 testClassArray2 = new TestClassArray2();
test2.Name = test.Name;
foreach (var item in test.Related1)
{
foreach (var item2 in item.Related2)
{
testClassArray2.id2 = item2.id2;
}
testClassArray.Related2.Add(testClassArray2);
}
test2.Related1.Add(testClassArray);
Console.WriteLine(test2);
testResult.Message = "New Result added successfullly....";
testResult.StatusCode = "201";
return testResult;
}
Swagger Input Sample Payload
Post Controller Result
Response of Sample input payload,(You can change it to default 201 response code as well)
I had a similar issue.
API method shows List was null
In my case a date field was not well formatted
So I use SimpleDateFormat on Android Studio with a correct datetime format
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss",Locale.US);
item.setDate(dateFormat.format(calendar.getTime()));
and works fine

How to map config in IConfigurationSection to a simple class

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

Static class can't use configuration Asp Net Core 2

I have a class from an older project that builds all my html emails. I pass in a view model to that method that has all the appropriate items. Those view models are built from the class shown. I want to use DI now that I'm using Asp.Net Core 2
public static class EmailViewModel
{
private static readonly string AppName = "Company Name";
private static readonly IAppConfiguration _appConfig;
public class AccountClosedEmailViewModel : IEmailViewModel
{
public AccountClosedEmailViewModel()
{
ApplicationName = AppName;
EmailName = "Account Closed";
}
public string FullName { get; set; }
public string EmailName { get; set; }
public string ApplicationName { get; set; }
public string Username { get; set; }
public string RenewRegistrationUrl { get; set; }
}
...rest removed for brevity
I want to use configuration from appsettings.json to bring in the values required from this class instead of setting them at the top of the class as private fields.
//don't want to do this
private static readonly string AppName = "Company Name";
//want to do this
private readonly IAppConfiguration _appConfig;
//and in the code use _appConfig.AppName
But because this is a static class, I can't have a constructor with a parameter to bring in the configuration. I am not a professional programmer so this issue has always stumped me. How do I get this to work? I've spent an inordinate amount of time trying various changes but still can't get it right.
Update per NightOwl888's suggestions:
I wrote a new class
public class EmailModel : IEmailModel
{
private static IAppConfiguration _appConfig;
private static IAzureConfiguration _azureConfig;
public string TemplateHtml { get; set; }
public EmailModel(IAppConfiguration config, IAzureConfiguration azureConfig)
{
_appConfig = config;
_azureConfig = azureConfig;
TemplateHtml = TemplateHtmlFromMaster();
}
public string TemplateHtmlFromMaster()
{
var filepath = Path.Combine(_azureConfig.AzureBaseBlob, _azureConfig.AzureEmailTemplatePath,
"EmailMaster.html");
return File.ReadAllText(filepath)
.Replace("{year}", DateTime.Now.Year.ToString()
.Replace("{applicationname)", _appConfig.AppName));
}
public class AccountClosedEmailViewModel : IEmailViewModel
{
public AccountClosedEmailViewModel()
{
ApplicationName = _appConfig.AppName;
EmailName = "Account Closed";
}
public string FullName { get; set; }
public string EmailName { get; set; }
public string ApplicationName { get; set; }
public string Username { get; set; }
public string RenewRegistrationUrl { get; set; }
}
}
I registered it in Startup.cs
services.AddSingleton<IEmailModel, EmailModel>();
But when I try to use it elsewhere
var em = new EmailModel();
It errs, and intellisense is showing that I need to pass in the two expected parameters. To be clear, this code is in the Services project in the solution (Class Library Asp.Net Core 2) and NOT in the Asp.Net Web Application project.
Any help in getting me to both understand how this works and to get it to work, is most appreciated.

ASP.NET Core can't get object from body

Code:
[HttpPost("user/register")]
public IActionResult Register([FromBody] User user)
{
if (user?.Name is null || user?.Password is null)
{
return BadRequest(new {message = "User is null and/or name and password are not provided"});
}
else
{
// Add to db
return Json(user);
}
}
Also the User class:
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Password { get; set; }
public string Role { get; set; }
}
It should basically get a user and add it to the database. I tried sending this json:
{ "Name": "Batman", "Password": "IronmanSucks"}
The app caught the request, but the user object was null. I also tried with:
{ "user": { "Name": "Batman", "Password": "IronmanSucks"} }
But according to this documentation, the first json should have worked fine.
Here is a link to an example http request in postman
Does this have to do with the headers or is it a bug in .NET Core 2.0?
This can only happen if the type does not have a parameterless constructor, thus this can be simply fixed by adding such.
I believe that the Model is coming up as invalid hence why it is null.
You should try adding a [BindNever] attribute into the User class for the Role and Guid properties, seeing as you aren't using them.
If that doesn't work you may try using extended classes like so:
public class User
{
public string Name { get; set; }
public string Password { get; set; }
}
public class DataUser : User
{
public Guid Id { get; set }
public string Role { get; set; }
}
If you're using MVC Core instead of MVC, make sure you add Json Formaters (from Microsoft.AspNetCore.Mvc.Formatters.Json). In your Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services
.AddMvcCore()
.AddJsonFormatters();
}
This should help the [FromBody] to de-serialize your Post content

MVC 6 Create View issues : There was an error creating the DBContext to get the model

Attempting to create a view for "Create" using the NavBar Model and the NavBarEntity shown below (in MVC6) receives this message...
There was an error running the selected code generator: There was an error creating the DBVContext instance to get the model... Value cannot be null... Parameter Name: connectionString
I picked this mode in View Wizard...
public class NavBarModel
{
public string ID { get; set; }
public List<LinkModel> Links { get; set; }
}
This DBContext class is shown here...
public class NavBarEntity : DbContext
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
public DbSet<List<LinkModel>> Links { get; set; }
}
And the LinkModel shown here..
public class LinkModel
{
public LinkModel()
{
ID = Guid.NewGuid().ToString();
}
[Key]
private string ID { get; set; }
public string HREF { get; set; }
public string Text { get; set; }
}
Configure Services looks like this...
var cfg2 = Configuration["Data Source=MyPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(cfg))
.AddDbContext<NavBarEntity>(options =>
{
options.UseSqlServer(cfg2);
});
Question: What am I doing wrong?
Thanks for the help listed above..
For newbies to MVC6 and EF7, the method named ConfigureServices, must contain a json pointer to the appsetting.json. That method is found in the Startup.cs file.
This is the services configuration to match the code shown above. The string value in the brackets points to the json location...
var cfg2 = Configuration["Data:DashboardContext:ConnectionString"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<DashboardContext>(options =>
{
options.UseSqlServer(cfg2);
})
But, you must also put a value into appsettings.json like this:
"Data": {
"DefaultConnection": {
"ConnectionString": "Server=(localdb)\\mssqllocaldb;Database=aspnet5-TestWebApplication1-d91c23e4-3565-476d-a7c0-45665bc0c367;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"DashboardContext": {
"ConnectionString": "Data Source= MYPC\\SQLEXPRESS;Initial Catalog=Dashboard;Integrated Security=True;Pooling=False"
}
},
The root cause of the Parameter Name: connectionString being null was that the appsettings.json has to be exactly as shown above. The json parsing routines must be able to locate the string name/value pair... Notice that these configurations fall under the "Data" name that contains other names. in this case "DefaultConnection" was there by default, and I added "DashboardContext" portion.
Also in MVC 6 you must change the connectionString type to IServiceProvider and NOT string as was done before...
public class DashboardContext : DbContext
{
public DashboardContext(IServiceProvider connectionString) : base (connectionString)
{}
public DbSet<NavBarEntity> NavBars { get; set; }
}
Alas: The Views created no problem...Yes!
The way you've tried to combine the DbContext and your entity isn't right. The DbContext should reference any entities you have as DbSets - entities should not inherit from it.
Your DbContext should look similar to this (EF6)
public class MyDbContext : DbContext
{
public MyDbContext(string connectionString)
: base(connectionString)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}
The constructor takes the name of the connecting string entry that's defined in your web.config you want to use. There are other ways to do this though - see here
Then create your entities as a simple class (POCO):
public class NavBarEntity
{
public NavBarEntity()
{
ID = Guid.NewGuid().ToString();
}
[Key]
public string ID { get; set; }
// Other properties/columns here
}
EDIT
My original answer was based on EF6 rather than EF7. Here's how I would implement the context in EF7 for completeness:
public class MyDbContext : DbContext
{
public MyDbContext(DbContextOptions<MyDbContext> options)
: base(options)
{ }
public DbSet<NavBarEntity> NavBars { get; set; }
// Other entities
}

Categories

Resources