ASP.NET MVC custom json config file - json

I have a json configuration file:
{
"chat": {
"host": "http://localhost:4555"
}
}
Also I have created class :
public class ChatConf
{
public String host { get; set; }
}
In my Startup file I'm doing this:
services.AddOptions();
services.Configure<ChatConf>(Configuration.GetSection("chat"));
The question is how can I get value of host in my main Layout?
Thanks.

you need to include Microsoft.Extension.IOption in your controller to work with the IOption collection. Then you can access your class by adding it to the constructor of you controller.
here is a good explanation

Related

Blazor DynamicComponent In .Net 6 And RenderFragment

Based on this article, I implemented the following code
https://blog.elmah.io/rendering-dynamic-content-in-blazor-wasm-using-dynamiccomponent/
There is a problem implementing the RenderFragment in the DynamicComponent
Error Image
File ComponentContainer.razor
<div #class="class">
#Container_Content
</div>
[Parameter]
public string class { get; set; };
[Parameter]
public RenderFragment RenderFragment_Content { get; set; }
File appsettings.json
{
"content": [
{
"type": "ComponentContainer",
"parameters": {
"class": "container"
"RenderFragment_Content": {
"type": "ComponentContainer",
"parameters": {
"class": "container"
}
}
}
]
}
File Index.razor
#using Microsoft.AspNetCore.Components;
#using Shahab.Client.Pages.Components
#using Shahab.Client.Pages.Components.Controls
#using Shahab.Client.Pages.Components.Controls.Container
#foreach (var Component in Components)
{
<DynamicComponent Type=#Component.type Parameters=#Component.parameters />
}
#code {
protected List<(Type type, Dictionary<string, object> parameters)> components { get; set; }
//OnInitializedAsync
protected async override Task OnInitializedAsync()
{
components = Configuration
.GetSection("content")
.GetChildren()
.Select(component =>
(
type: Type.GetType($"Shahab.Client.Pages.Components.Controls.Container.{component.GetValue<string>("type")}"),
parameters: component
.GetSection("parameters")
.GetChildren()
.ToDictionary(p => p.Key, p => p.Get<object>())
)
)
.ToList();
}
}
enter image description here
My goal is to put the components together in runtime, even nested.(like page designer sharepoint)
There are two incomplete ways to do this.
1- Using DynamicComponent
2- Using RenderTreeBuilder
Both of these solutions do not work in practice when used in nested(RenderFragment) components.
And a complete way to generate files in run time.(generate .razor)
But the problem with this method is that it is compile in runtime.
(in asp.net web-form and asp.net core/mvc you can use AddRazorRuntimeCompilation method But not work in Blazor)
What is your solution to this challenge ?
You try to deserialize the following to a RenderFragment:
{
"type": "ComponentContainer",
"parameters": {
"class": "container"
}
}
Firstly, you can't make a simple deserialisation from the following to a RenderFragment as RenderFragment is a Delegate type.
Secondly, I don't see how this would match the RenderFragment type if it could be deserialized.
DynamicComponent Parameters accept a dictionary,So you need to create a RenderFragment delegate to add to the dictionary

AppSettings Deserialize to an unknown or dynamic class

I'm trying to set up a series of complex app settings in a separate settings.json file - I won't go into detail as to why...
So I have a JSON file which looks like this:
{
"Website": {
"Name": "Website Name",
"api_key": "----------",
"domain": "-----------"
},
"Pages": {
"Index": {
"Name": "Index",
"Widgets": {
"BestSellers": {
"Name": "BestSellers",
"Type": "ProductCollection",
"Data": {
"Limit": "8",
"Sort": {
"SortType": 3
},
"GetFullProducts": true,
"GroupVariations": false
}
}
}
}
}
}
The first section "Website" simply fetches string settings, all working fine.
The section section "Pages" is more complicated. I have classes that look like this:
public class PageSettings
{
public string Name { get; set; }
public Dictionary<String, Widget> Widgets { get; set; }
public class Widget
{
public string Name { get; set; }
public string Type { get; set; }
public Dictionary<String, object> Data { get; set; } // THIS IS THE PROPERTY THIS QUESTION IS ABOUT
}
}
I use this code to deserialise the above:
IConfigurationSection pagessection = root.GetSection("Pages");
if (pagessection.Exists())
{
_Pages = new Dictionary<String, PageSettings>();
pagessection.Bind(_Pages);
}
With the JSON File exactly as above, this will fail. For some reason, the nested Object Sort in the Data property cannot be deserialised as Object:
"Sort": {
"SortType": 3
}
If I take the above nested object out then all the code so far will work. However, there are use cases where I need that nested object.
I have tried using ExpandoObject which is very cool and clever, but because it expects KeyValuePairs, it then only serialises the nested object in Data, ignoring the simple properties Limit, GetFullroduct etc.
So what I need is a form of ExpandoObject which can also be ExpandoString or something?!
Alternatively... I need to be able to get the Data property from the settings.json file in String form and explicitly deserialise it using JsonConvert.Deserialize at the point of use, because at that point I can declare the proper class that it needs to be deserialised to, but i can't seem to find a way to get the IConfigurationSection code to get the value as a string, rather than a JSON object.
I can't find a solution to this, the nested object breaks everything I have tried.
The helpful comment from #Fei Han has helped a little in highlighting the flexibility of the JObject class, so the solution I have come to is this:
The complex class has to be stored as an HTML encoded string in the settings.json file:
"Data": "{"Limit": "8","GetFullProducts":true,"GroupVariations":true, "Sort": {"SortType": 3}}"
it has to be HTMLEncoded because it is the only way I can find to make the ConfigurationBuilder treat it as a string so that I can cast it correctly later.
The corresponding Class for this now has these properties:
public string ModelString { get; set; }
public Newtonsoft.Json.Linq.JObject Model
{
get
{
string s = ModelString.HtmlDecode();
if (s.EmptyStr())
{
return new JObject();
} else {
return JObject.Parse(s);
}
}
}
From this I am able to easily cast my Data to the eventually required class using .ToObject<MyObject>()
For some reason, this works. I am able to deserialise the string to a JObject in this method, but not directly using the Bind command on IConfigurationSection.
If anyone has any tips on why Bind won't do it, that'd be interesting!

How do I set up my model class for nested JSON responses?

I'm using the HttpClient in a C# application to access a web API. I'm new to this kind of web API (I usually do WCF services).
One of the responses looks like this:
{
"access_token": the access token,
"scope": "read",
"expires_in": seconds to expiry,
"refresh_token": a refresh token
}
And the model class looks like this:
class AuthResponse
{
public string access_token { get; set; }
public string scope { get; set; }
public int expires_in { get; set; }
public string refresh_token { get; set; }
}
So when I do:
var result = resposne.Content.ReadAsAsync<AuthResponse>().Result;
I get an AuthResponse object back with its values filled in. Magic.
The next bit of API returns a response like this:
{
"data": {
"visible": boolean,
"email": valid e-mail string or null,
"location_string": human-readable location identifier string,
"ad_id": primary key of the ad
},
"actions": {
"change_form": URL to change this ad
"public_view": URL to view this ad's public HTML page
"html_edit": URL to view this ad's HTML edit page
that has more options than the API change_form does
}
}
What would my model class look like? How do I account for the nesting?
If you haven't solved this by now this is what I have found:
I found this to be great:
http://json2csharp.com
You simply paste your JSON url or even your JSON into it and it will return you a model class, with nesting. Alternatively you can copy your JSOn and Edit > Paste Special > in Visual studio, which will return your model class too.

View page generates RuntimeBinderException, works anyway

I am trying to use ServiceStack Razor in my project. I set up a very simple DTO:
namespace ModelsWeb.Diagnostics
{
[Route("/echo")]
[Route("/echo/{Text}")]
public class Echo
{
public string Text { get; set; }
}
public class EchoResponse
{
public ResponseStatus ResponseStatus { get; set; }
public string Result { get; set; }
}
}
And a service to go with it:
namespace Rest.Services
{
public class EchoService : Service
{
public object Any(Echo request)
{
return new EchoResponse {Result = request.Text};
}
}
}
Note that the DTO and the service are in different namespaces. This is because I'm building two applications at once -- the server and the thick client -- and I put all the DTOs in a separate class library that they both depend on. This way, the client can reference just that class library, and no other server-side code. I am using Razor to provide a Web interface to some of the server functionality.
Anyway, I also wrote a simple view for my Echo service:
#using ServiceStack.Razor
#using ModelsWeb.Diagnostics
#inherits ViewPage<EchoResponse>
#{
ViewBag.Title = "Echo Response";
Layout = "BasePage";
}
<h1>You typed: #Model.Result</h1>
When I type "http://localhost:62061/echo/hello2" into the browser, I get an error on my log:
Cannot implicitly convert type 'ServiceStack.Razor.Compilation.RazorDynamicObject'
to 'ModelsWeb.Diagnostics.EchoResponse'
However, the template still works, and I see the expected result in the browser. What's going on here ? Am I doing anything wrong ? If not, how can I suppress this exception ?

How to read appsettings.json in my _layout.chtml

I cannot seem to figure out how to read values from the appsettings.json in my _Layout.chtml file.
Is it not just available, something like this?
#Configuration["ApplicationInsights:InstrumentationKey"]
I created a new MVC project using razor pages.
fyi, i'm an mvc newbee - code samples help a lot.
In .net core mvc you can inject the configuration by adding the following two lines at the top of your view:
#using Microsoft.Extensions.Configuration
#inject IConfiguration Configuration
You can then access the value like this:
#Configuration.GetSection("ApplicationInsights")["InstrumentationKey"]
If you use the options pattern you can inject them into your view like this:
#using Microsoft.Extensions.Options
#inject IOptions<ApplicationInsightsOptions>
ApplicationInsightsOptionsAccessor
#
{
var instrumentationKey =
ApplicationInsightsOptionsAccessor.Value.InstrumentationKey;
}
Options pattern in ASP.NET Core
Using ActionFilters you can interrupt the request and add the configuration variables maybe to the ViewBag so it becomes accessible from the views or from the _Layout.cshtml File.
For example, if the following configuration section is inside your appsettings.json
{
"MyConfig": {
"MyValue": "abc-def"
}
}
In the code MyConfig.cs would be:
public class MyConfig
{
public string MyValue{ get; set; }
}
First create a very simple ActionFilter which derives from IAsyncActionFilter as following :
public class SampleActionFilter : IAsyncActionFilter
{
private MyConfig _options;
public SampleActionFilter(IConfiguration configuration)
{
_options = new MyConfig();
configuration.Bind(_options);
}
public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
((Microsoft.AspNetCore.Mvc.Controller)context.Controller).ViewBag.MyConfig = _options;
await next();
}
}
Later in the Startup.ConfigureServices method change services.AddMvc to the following:
public void ConfigureServices(IServiceCollection services)
{
//..........
services.AddMvc(options=>
{
options.Filters.Add(new SampleActionFilter(
Configuration.GetSection("MyConfig")
));
});
//..........
}
To access the values just simply in the _Layout.cshtml or other view you can type:
#ViewBag.MyConfig.MyValue