Get JsonOptions from controller - c#

I set to indent JSON in Startup class, but how do I retrieve the formatting value from a controller?
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddWebApiConventions()
.AddJsonOptions(options=> options.SerializerSettings.Formatting=Newtonsoft.Json.Formatting.Indented);
}
}
public class HomeController : Controller
{
public bool GetIsIndented()
{
bool isIndented = ????
return isIndented;
}
}

You can just inject an instance of IOptions<MvcJsonOptions> into your Controller, like so:
private readonly MvcJsonOptions _jsonOptions;
public HomeController(IOptions<MvcJsonOptions> jsonOptions, /* ... */)
{
_jsonOptions = jsonOptions.Value;
}
// ...
public bool GetIsIdented() =>
_jsonOptions.SerializerSettings.Formatting == Formatting.Indented;
See the docs for more information about IOptions (the Options pattern).
If all you care about is the Formatting, you can simplify slightly and just use a bool field, like so:
private readonly bool _isIndented;
public HomeController(IOptions<MvcJsonOptions> jsonOptions, /* ... */)
{
_isIndented = jsonOptions.Value.SerializerSettings.Formatting == Formatting.Indented;
}
In this example, there's no need for the GetIsIndented function.

One option is to create a class where you declare the current configuration values
public class MvcConfig
{
public Newtonsoft.Json.Formatting Formatting { get; set; }
}
Then instantiate it in the configure method where you also register the class as a singleton
public void ConfigureServices(IServiceCollection services)
{
var mvcConfig = new MvcConfig
{
Formatting = Newtonsoft.Json.Formatting.Indented
};
services.AddMvc()
.AddWebApiConventions()
.AddJsonOptions(options=> options.SerializerSettings.Formatting=mvcConfig.Formatting);
services.AddSingleton(mvcConfig);
}
Then inject it in the controller and use it
public class HomeController : Controller
{
private readonly MvcConfig _mvcConfig;
public HomeController(MvcConfig mvcConfig)
{
_mvcConfig = mvcConfig;
}
public bool GetIsIndented()
{
return _mvcConfig.Formatting == Newtonsoft.Json.Formatting.Indented;
}
}

Related

.NET Core Dependency Injection how to handle multiple objects

As the title says I have a .NET Core application that I am trying to convert over to and take advantage of the built in Microsoft Dependency Injection.
I have an object and a base class for the object, call it CommunicationBase and Communicator. When my app starts up and reads the configuration file, I can have N number of objects to instantiate.
Previously, before switching to Dependency Injection, somewhere in my startup routine, where I read the configuration file, I would have a List<CommunicationBase> variable that I would instantiate and add Communicator objects to and at the same time, set some of the base properties, which changed based on how many were in my configuration and each ones properties in config.
How would I achieve this with DI?
I understand that in my services, I would register the type so it can be injected into other class constructors.
For example, services.AddTransient<CommunicationBase, Communicator>(); but as I understand it, this just registers the types with DI. I can inject it into a class and have a random instance of one of them.
How would I then have N number of instances and be able to set properties of each one as I create the instance?
Or, is this a scenario where DI is not necessary or won't work and I need to just do it the way I was doing it before?
Thanks!
I would slightly modify approach shown here. So I would define some enum that would then be used to decide what instance to return.
Sample classes setup and the enum:
public enum CommuniationType
{
False, True, Other,
}
public abstract class CommunicationBase
{
public CommunicationBase(CommuniationType communiationType)
{
CommuniationType = communiationType;
}
public bool IsConnected { get; set; }
public CommuniationType CommuniationType { get; protected set; }
}
public class Communicator : CommunicationBase
{
public Communicator(CommuniationType communiationType) : base(communiationType) { }
}
Now, in the place where you have access to service collection (e.g. in ASP.NET the place would be Stratup.RegisterServices method) you define your objects of concrete class and register them, as in the sample code below (at the bottom, there are also test classes using CommunicationBase object for testing puproses):
public class Program
{
static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
SetupNObjects(serviceCollection);
serviceCollection.AddTransient<CommunicationBaseServiceResolver>(serviceProvider => communicationType =>
{
var implementations = serviceProvider.GetServices<CommunicationBase>();
return implementations.First(x => x.CommuniationType == communicationType);
});
serviceCollection.AddScoped<FalseTestClass>();
serviceCollection.AddScoped<TrueTestClass>();
var serviceProvider = serviceCollection.BuildServiceProvider();
var f = serviceProvider.GetService<FalseTestClass>();
var t = serviceProvider.GetService<TrueTestClass>();
}
// Here you should take care of registering objects, after reading config.
// That would be best place to do that.
static void SetupNObjects(ServiceCollection serviceCollection)
{
var comFalse = new Communicator(CommuniationType.False);
comFalse.IsConnected = false;
var comTrue = new Communicator(CommuniationType.True);
comTrue.IsConnected = true;
serviceCollection.AddScoped<CommunicationBase>((serviceProvider) => comFalse);
serviceCollection.AddScoped<CommunicationBase>((serviceProvider) => comTrue);
}
}
public class FalseTestClass
{
private readonly CommunicationBase communication;
public FalseTestClass(CommunicationBaseServiceResolver resolver)
{
communication = resolver(CommuniationType.False);
}
}
public class TrueTestClass
{
private readonly CommunicationBase communication;
public TrueTestClass(CommunicationBaseServiceResolver resolver)
{
communication = resolver(CommuniationType.True);
}
}
Firstly do you need to has clear the differences between Transient, Scoped, Singleton lifetime. To understand how works with the list of Communicator objects that will be read from your configuration file.
One approuch to resolve your question is
Create an interface ICommunicatorList with one method to get a List, i mean you can envolve the list of communicators.
Create a clase that inherits from ICommunicatorList (for example called CommunicatorList), with a private field for your list of Communicators. On the constructor method set your private field with the list of communicator, o here you can receive like a parameter from the section of the config file to iterate and full your private field.
on this class implement your code to return the list of communicators.
Now, in your startups file you can now create the service
services.AddTransient< ICommunicatorList>(x => new CommunicatorList(parameters));
I would do it the following way.
First you have communicators and settings classes:
namespace WebApiApp
{
public abstract class CommunicationBase
{
public abstract string Communicate();
}
public class Communicator1Settings
{
public string Parameter { get; set; }
}
public class Communicator1 : CommunicationBase
{
private readonly string parameter;
public Communicator1(string parameter)
{
this.parameter = parameter;
}
public override string Communicate()
{
return $"Type: {nameof(Communicator1)}, parameter: {this.parameter}";
}
}
public class Communicator2Settings
{
public string Parameter1 { get; set; }
public string Parameter2 { get; set; }
}
public class Communicator2 : CommunicationBase
{
private readonly string parameter1;
private readonly string parameter2;
public Communicator2(string parameter1, string parameter2)
{
this.parameter1 = parameter1;
this.parameter2 = parameter2;
}
public override string Communicate()
{
return $"Type: {nameof(Communicator1)}, parameter1: {this.parameter1}, parameter2: {this.parameter2}";
}
}
public class CommunicatorsSettings
{
public List<Communicator1Settings> Communicators1 { get; set; }
public List<Communicator2Settings> Communicators2 { get; set; }
}
}
In appsettings.json you have the configuration of communicators:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"Communicators": {
"Communicators1": [
{
"Parameter": "First communicator1 parameter"
},
{
"Parameter": "Second communicator1 parameter"
}
],
"Communicators2": [
{
"Parameter1": "First communicator2 parameter1",
"Parameter2": "First communicator2 parameter2"
},
{
"Parameter1": "Second communicator2 parameter1",
"Parameter2": "Second communicator2 parameter2"
}
]
}
}
So you have two instances of Communicator1 with different parameters and two instances of Communicator2 with different parameters as well.
Then, you configure the container. The following is the content of program.cs for .net 6:
using WebApiApp;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
AddCommunicators();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.Run();
void AddCommunicators()
{
var settings = new CommunicatorsSettings();
builder.Configuration.Bind("Communicators", settings);
foreach (var communicatorSettings in settings.Communicators1)
{
builder.Services.AddScoped<CommunicationBase>(
_ => new Communicator1(communicatorSettings.Parameter));
}
foreach (var communicatorSettings in settings.Communicators2)
{
builder.Services.AddScoped<CommunicationBase>(
_ => new Communicator2(communicatorSettings.Parameter1, communicatorSettings.Parameter2));
}
}
Now you can inject IEnumerable<CommunicationBase> into your controller:
using Microsoft.AspNetCore.Mvc;
namespace WebApiApp.Controllers
{
[ApiController]
[Route("[controller]")]
public class CommunicatorsController : Controller
{
private readonly IEnumerable<CommunicationBase> communicators;
public CommunicatorsController(IEnumerable<CommunicationBase> communicators)
{
this.communicators = communicators;
}
public IActionResult Get()
{
var result = this.communicators.Select(x => x.Communicate());
return this.Json(result);
}
}
}
This is the result for /communicators web API:
[
"Type: Communicator1, parameter: First communicator1 parameter",
"Type: Communicator1, parameter: Second communicator1 parameter",
"Type: Communicator1, parameter1: First communicator2 parameter1, parameter2: First communicator2 parameter2",
"Type: Communicator1, parameter1: Second communicator2 parameter1, parameter2: Second communicator2 parameter2"
]

(Interface) A circular dependency was detected for the service of type

I have 2 interfaces:
public interface IPedidoService
{
UsuarioDrogueria CUsuarioDrogueria(string userId, int idDrogueria);
List<PedidoComboProducto> CPedidosCombosProductos(int idcombo, int idPedido);
}
public interface IEmailService
{
void SendEmailAttachment(string email, string subject, string archive);
void SendNotificationEmail(List<Pedido> pedidos, string email, Drogueria drog);
void SendNotificationEmailADM(Pedido pedido) ;
}
I want to use the functions from IEmailService inside IPedidoService, so I inject it in its constructor when I create the respository.
public class PedidoService : IPedidoService
{
private readonly IEmailService emailService;
public PedidoService(IEmailService e)
{
this.emailService = e;
}
}
Up until here everything works fine, but when I try to do reverse the roles (IPedidoService functions inside IEmailService):
public class EmailService : IEmailService
{
private readonly IPedidoService pedidoSettings;
public EmailService(IPedidoService p)
{
this.pedidoSettings = p;
}
}
I end up getting this exception:
System.InvalidOperationException: A circular dependency was detected for the service of type
'EnvioPedidos.Data.Abstract.IPedidoService'.
EnvioPedidos.Data.Abstract.IPedidoService(EnvioPedidos.PedidoService) ->
EnvioPedidos.Data.Abstract.IEmailService(EnvioPedidos.EmailService) ->
EnvioPedidos.Data.Abstract.IPedidoService
Can anybody help me trace the issue here?
A simple way is to use Lazy<T> class which is based on this blog:
Custom extension method:
public static class LazyResolutionMiddlewareExtensions
{
public static IServiceCollection AddLazyResolution(this IServiceCollection services)
{
return services.AddTransient(
typeof(Lazy<>),
typeof(LazilyResolved<>));
}
}
public class LazilyResolved<T> : Lazy<T>
{
public LazilyResolved(IServiceProvider serviceProvider)
: base(serviceProvider.GetRequiredService<T>)
{
}
}
Configure in Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
//services.AddSingleton<IPedidoService, PedidoService>();
//services.AddSingleton<IEmailService, EmailService>();
services.AddLazyResolution();
}
Change your implements class:
public class PedidoService : IPedidoService
{
private readonly Lazy<IEmailService> emailService;
public PedidoService(Lazy<IEmailService> e)
{
this.emailService = e;
}
//...
}
public class EmailService : IEmailService
{
private readonly Lazy<IPedidoService> pedidoSettings;
public EmailService(Lazy<IPedidoService> p)
{
this.pedidoSettings = p;
}
//...
}
When you have 2 classes, they cannot reference each other by dependency injection. This is called a circular dependency, as shown by your error. You need a 3rd class that references both services and you can use the methods there.
public class PedidoService
{
public PedidoService()
{
}
}
public class EmailService
{
public EmailService()
{
}
}
public class Container
{
private readonly EmailService emailService;
private readonly PedidoService pedidoService;
public Container(EmailService emailService, PedidoService pedidoService)
{
this.emailService = emailService;
this.pedidoService = pedidoService;
}
//use the services here
}

.NET Core Configuration is forcing me to DependencyInjection

I want to read appsettings.json non-controller class.Consider has a DatabaseUtil and contain a static connect() method. I need to connectionString for connection and i'm getting this from appsettings.json.This operation piece of cake in the startup.cs:)
Like this:
Configuration.GetConnectionString("HangfireDBConn")
Also it can be at the controller side with dependcy injection.But my problem which want to reach appSettings from DatbaseUtil class.
appSettings.json:
"NotifySettings": {
"DbConnection": "abc",
"Email": "abc#domain.com",
"SMTPPort": "5605"
}
Then i created my configuration settings class:
public class NotifySettings
{
public string DbConnection { get; set; }
public string Email { get; set; }
public string SMTPPort { get; set; }
}
And I added dependency for constructor injection to DatabaseUtil class and added IDatabaseUtil
public class DatabaseUtil : IDatabaseUtil
{
private static NotifySettings _NotifySettings;
public DatabaseUtil(IConfiguration _iconfig)
{
_NotifySettings = _iconfig.GetSection("NotifySettings").Get<NotifySettings>();
}
public static String ConnectToDatabase()
{
return "MESSAGE :" + _NotifySettings.DbConnection;
}
}
}
And i added DatabaseUtil to startup.cs
services.AddScoped<IDatabaseUtil, DatabaseUtil>();
and finally i injected IDatabaseUtil to my controller class and i can reach mysettings end of the this work.
Yes i can but not best way!
Let the join my Brain Storming :) ; If i have to inject to IDatabaseUtil every class where i want to use db helper methods.But if i had a static method in this class just it need to this line of code:
DatabaseUtils.connect();
That's feels me like i wrote unnecessary code.
What do you think about my approximation.Which one is best way for this case ?
change
services.AddScoped<IDatabaseUtil, DatabaseUtil>();
to
services.AddSingleton<IDatabaseUtil, DatabaseUtil>();
This way you only have one instance of DatabaseUtil
I'm still not entirely clear, but if the need here is to make values from your Configuration statically available, then copy them from your configuration to a static class during the startup:
public static class GlobalSettings
{
public static string ConnectionString { get; set; }
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
GlobalSettings.ConnectionString = Configuration.GetSection("ConnectionString").Value;
// ...
}
}
If you need to get the config and do the assignment from somewhere else, use the ConfigurationBuilder:
var config = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
using System;
using Microsoft.Extensions.Configuration;
namespace project.Utility
{
public class ConnectionString
{
private IConfigurationRoot _config;
private static ConnectionString _internalInstance;
public static ConnectionString Instance
{
get
{
return _internalInstance;
}
}
public static void Init(IConfigurationRoot config)
{
_internalInstance = new ConnectionString();
_internalInstance._config = config;
}
public String Get(string key)
{
var NotifySettings =
Instance._config.GetSection(key).Get<NotifySettings>();;
return NotifySettings;
}
}
}
// call this above method from any place like controller or class file by below code
// use refernece of the namespace
ConnectionString connectionString = new ConnectionString(); // object creation
NotifySettings settings = connectionString.Get("NotifySettings"); // call with your key value get the settings object
Try this it should work let me know if any issues i can help on that

Getting values from appsettings.json

I tried getting value from my appsettings.json which is modeled like this:
"ConfigurationModel": {
"RfidAddress": "172.23.19.73",
"BaudRate": "152000",
"DataManagerTimeOut": "32000"
}
Then I created a POCO like so:
public class ConfigurationModel
{
public string RfidAddress { get; set; }
public int BaudRate { get; set; }
public int DataManagerTimeOut { get; set; }
}
In my Startup.cs I added the ConfigurationModel in the services like so:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<ConfigurationModel>(Configuration.GetSection("configurationModel"));
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddSignalR();
}
I created a class that utilized this settings like this:
public class RfidClass
{
private readonly ConfigurationModel _configurationModel;
public RfidClass(IOptions<ConfigurationModel> configurationModel)
{
_configurationModel = configurationModel.Value;
}
public void sas()
{
Console.WriteLine(_configurationModel.RfidAddress);
}
}
Then in my Program.cs I need to call that class that I have just created like this:
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
SetRfid();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
public static void SetRfid()
{
var rfidClass = new RfidClass(); <-- MISSING ARGUMENT
}
}
How can I pass the needed parameter to instantiate my RfidClass?
You should be able to extract the value by setting the result from the .Build() as follows:
public class Program
{
public static void Main(string[] args)
{
var host = CreateWebHostBuilder(args).Build();
host.Run();
var config = host.Services.GetService<IConfiguration>();
var configSection = config.GetSection("ConfigurationModel");
var configModel = configSection.Get<ConfigurationModel>();
SetRfid(configModel);
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>();
public static void SetRfid(ConfigurationModel configModel)
{
var rfidClass = new RfidClass(configModel); <-- ADDED
}
}
I'm bit scared to say that Program and Startup classes are not meant to do such things. Usually such operations are called from some other classes, i.e. Controller classes. Post this, you can use dependency injection to pass the objects. Here is the code to do that:
public void ConfigureServices(IServiceCollection services) {
... services.Configure(Configuration.GetSection("configurationModel")); services.AddSingleton(m => { return new RfidClass(m.GetService>()); }); ... }
And here is the controller code:
public HomeController(ILogger<HomeController> logger, RfidClass rfidClass)
{
...
}
You appear to have everything set up correctly to use the Options Pattern, but are then trying to new up RfidClass rather than inject the options into a class like a Controller, View, Handler, or Middleware. These classes are unique in the sense that the framework can inject services into them.
public class HomeController : Controller
{
private readonly RfidClass _rfidClass;
public HomeController(IOptionsMonitor<ConfigurationModel> options)
{
_rFidClass= options.CurrentValue;
}
public IActionResult Index()
{
var rfidAddress = _rfidClass.rfidAddress;
var baudRate = rfidClass.BaudRate;
// do stuff.
return View();
}
}
There is some great information int he microsoft documentation on utilizing the options pattern here https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-3.1.

Resolve Dependencies inside Startup.cs

I have a new Asp.Net core application that has the following entry in the appsettings.json file:
{
"DatabaseConnections": {
"DatabaseUri": "https://localhost:8081",
"ApplicationKey": "C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==",
"DatabaseName": "MyDatabase"
}
}
I'm attempting to pull the data out to use during the ConfigureServices method, using the .Bind method:
public class DatabaseConnections
{
public string DatabaseUri { get; set; }
public string ApplicationKey { get; set; }
public string DatabaseName { get; set; }
}
private DatabaseConnections databaseSettings;
private DatabaseConnections DatabaseSettings
{
get
{
if (databaseSettings == null)
{
databaseSettings = new DatabaseConnections();
Configuration.Bind(databaseSettings);
}
return databaseSettings;
}
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IDocumentClient>(
new DocumentClient(
new Uri(DatabaseSettings.DatabaseUri),
DatabaseSettings.ApplicationKey));
}
However, when I perform the binding, the settings are all set to null. But if I try to do it without the model binding, it seems to work fine:
public void ConfigureServices(IServiceCollection services)
{
var databaseSettings = Configuration.GetSection("DatabaseConnections");
services.AddSingleton<IDocumentClient>(
new DocumentClient(
new Uri(databaseSettings.GetValue<string>("DatabaseUri")),
databaseSettings.GetValue<string>("ApplicationKey")));
}
What am I doing wrong?
You can either build a service provider or use string.
Configuration.GetSection("DatabaseConnections:DatabaseUri").Value
For example,
public void ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<DatabaseConnections>(
Configuration.GetSection("DatabaseConnections"));
var sp = services.BuildServiceProvider();
var databaseConnections = sp.GetService<IOptions<DatabaseConnections>>();
services.AddSingleton<IDocumentClient>(
new DocumentClient(new Uri(databaseConnections.Value.DatabaseUri)),
databaseConnections.Value.ApplicationKey));
}
Controller
public class HomeController : Controller
{
private readonly DatabaseConnections _databaseConnections;
public HomeController(IOptions<DatabaseConnections> databaseConnections)
{
_databaseConnections = databaseConnections.Value;
}
}
The other answer is good if you want to use IOptions, for whatever reason I really don't like doing it and prefer binding to a class.
You can do this with a single line in your Configure Services method :
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(Configuration.GetSection("DatabaseConnections").Get<DatabaseConnections>());
}
It looks like you are missing the "Get" on the end which takes the configuration section and binds it to your class.
Further info : http://dotnetcoretutorials.com/2016/12/26/custom-configuration-sections-asp-net-core/

Categories

Resources