Resolve Dependencies inside Startup.cs - c#

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/

Related

.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.

Injecting multiple connection strings in constructor c#

I have a use case where I will need multiple connection strings in my data access layer and will use anyone depending on the input.
Currently, I have 2 connection strings which I have added in JSON and then I am injecting both.
Is there any other solution to inject all the connection strings at once because in future with the introduction of any new DB I have to add one more connection string in JSON and then again inject it?
StartUp class:
private static void Main(string[] args)
{
ServiceCollection serviceCollection = new ServiceCollection();
ConfigureServices(serviceCollection);
IServiceProvider serviceProvider =
serviceCollection.BuildServiceProvider();
serviceProvider.GetService<StudentApp>().Start();
}
private static void ConfigureServices(IServiceCollection
serviceCollection)
{
IConfigurationRoot configuration = GetConfiguration();
Database database1 = new SqlDatabase(configuration.GetSection("Configuration:ConnectionString1").Value;
Database database2 = new SqlDatabase(configuration.GetSection("Configuration:ConnectionString2").Value;
// Here I am doing Multiple injections
serviceCollection.AddSingleton(database1);
serviceCollection.AddSingleton(database2);
serviceCollection.AddOptions();
serviceCollection.Configure<AppSettings(configuration.GetSection("Configuration"));
serviceCollection.AddSingleton(configuration);
serviceCollection.AddTransient<IStudentDataAccess,StudentDataAccess>();
serviceCollection.AddTransient<StudentApp>();
}
private static IConfigurationRoot GetConfiguration()
{
return new ConfigurationBuilder()
.AddJsonFile("appsettings.json", optional: true)
.Build();
}
StudentApp Class:
private readonly IStudentDataAccess _dataAccess;
private readonly AppSettings _config;
private readonly Database _database1;
private readonly Database _database2;
public StudentApp(IStudentDataAccess dataAccess,IOptions<AppSettings>
config, Database database1, Database database2)
{
_dataAccess= dataAccess;
_config = config.Value;
_database1 = database1;
_database2 = database2;
}
public void Start()
{
int count= _dataAccess.GetStudentCount(deptId);
}
DataAccess classes:
public interface IStudentDataAccess
{
int GetStudentCount(int deptId);
}
public class StudentDataAccess : IStudentDataAccess
{
private readonly AppSettings _config;
private readonly Database _database1;
private readonly Database _database2;
public StudentDataAccess (IOptions<AppSettings> config, Database
database1,Database database2)
{
_config = config.Value;
_database1 = database1;
_database2 = database2;
}
public int GetStudentCount(int deptId)
{
// Execute queries either by Database1 or 2.
}
}
Database class used is from Microsoft.Practices.EnterpriseLibrary.Data.
How can I avoid creating multiple Singleton classes for different connection strings?
Any help?
You can keep your connection strings as an array in your appsettings.json:
{
...
"ConnectionStrings": [
{
"Name": "ConnectionString1",
"Value": "some value"
},
{
"Name": "ConnectionString1",
"Value": "some value"
}
]
}
and map them to some class using Options pattern:
public class ConnectionStringOptions
{
public ConnectionString[] ConnectionStrings { get; set; }
}
public class ConnectionString
{
public string Name { get; set; }
public string Value { get; set; }
}
And then, you can have an interface like this one:
public interface IDatabaseProvider
{
IEnumerable<Database> GetDatabases();
Database GetDatabase(string name);
}
with the implementation like this
public class DatabaseProvider : IDatabaseProvider
{
private readonly ConnectionStringOptions _options;
public DatabaseProvider(IOptions<ConnectionStringOptions> optionsAccessor)
{
this._options = optionsAccessor.Value;
}
public IEnumerable<Database> GetDatabases()
{
foreach (ConnectionString connectionString in this._options.ConnectionStrings)
yield return new SqlDatabase(connectionString.Value);
}
public Database GetDatabase(string name)
{
string connectionString = this._options.ConnectionStrings.SingleOrDefault(x => x.Name == name).Value;
return new SqlDatabase(connectionString);
}
}
Now you just register the IDatabaseProvider:
serviceCollection.AddTransient<IDatabaseProvider, DatabaseProvider>()
and inject it in your services as needed. E.g:
public class StudentApp
{
private readonly IEnumerable<Database> _databases;
public StudentApp(IStudentDataAccess dataAccess, IDatabaseProvider databasesProvider)
{
//Or get just the one you want by name
this._databases = databasesProvider.GetDatabases();
// ...
}
// ...
}
Update: Code snippets for Options pattern:
serviceCollection.Configure<ConnectionStringOptions>(configuration.GetSection("ConnectionStringsā€¯));

Get JsonOptions from controller

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;
}
}

Read a value from appsettings.json in 1.0.0-rc1-final

In one of my concrete class. I have the method.
public class Call : ICall
{
......
public Task<HttpResponseMessage> GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri("http://localhost:30151");
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
Now I put the url into appsettings.json.
{
"AppSettings": {
"uri": "http://localhost:30151"
}
}
And I created a Startup.cs
public class Startup
{
public IConfiguration Configuration { get; set; }
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
Configuration = builder.Build();
}
}
and now I get stuck.
EDIT
By the way, I don't have a controller, it is a console application.
The preferred way to read configuration from appSettings.json is using dependency injection and the built or (or 3rd party) IoC container. All you need is to pass the configuration section to the Configure method.
public class AppSettings
{
public int NoRooms { get; set; }
public string Uri { get; set; }
}
services.Configure<AppSettings>(Configuration.GetSection("appsettings"));
This way you don't have to manually set the values or initialize the AppSettings class.
And use it in your service:
public class Call : ICall
{
private readonly AppSettings appSettings;
public Call(IOptions<AppSettings> appSettings)
{
this.appSettings = appSetings.Value;
}
public Task<HttpResponseMessage>GetHttpResponseMessageFromDeviceAndDataService()
{
var client = new HttpClient();
var uri = new Uri(appSettings.Uri);
var response = GetAsyncHttpResponseMessage(client, uri);
return response;
}
}
The IoC Container can also be used in a console application, you just got to bootstrap it yourself. The ServiceCollection class has no dependencies and can be instantiated normally and when you are done configuring, convert it to an IServiceProvider and resolve your main class with it and it would resolve all other dependencies.
public class Program
{
public static void Main(string[] args)
{
var configurationBuilder = new ConfigurationBuilder()
.AddJsonFile("appsettings.json");
var configuration = configurationBuilder.Build()
.ReloadOnChanged("appsettings.json");
var services = new ServiceCollection();
services.Configure<AppSettings>(configuration.GetSection("appsettings"));
services.AddTransient<ICall, Call>();
// add other services
// after configuring, build the IoC container
IServiceProvider provider = services.BuildServiceProvider();
Program program = provider.GetService<Program>();
// run the application, in a console application we got to wait synchronously
program.Wait();
}
private readonly ICall callService;
// your programs main entry point
public Program(ICall callService)
{
this.callService = callService;
}
public async Task Run()
{
HttpResponseMessage result = await call.GetHttpResponseMessageFromDeviceAndDataService();
// do something with the result
}
}
Create a static class
public static class AppSettings
{
public static IConfiguration Configuration { get; set; }
public static T Get<T>(string key)
{
if (Configuration == null)
{
var builder = new ConfigurationBuilder().AddJsonFile("appsettings.json");
var configuration = builder.Build();
Configuration = configuration.GetSection("AppSettings");
}
return (T)Convert.ChangeType(Configuration[key], typeof(T));
}
}
then access the settings anywhere you want like
var uri = AppSettings.Get<string>("uri");
var rooms = AppSettings.Get<int>("noRooms");
appsettings.json example
{
"AppSettings": {
"uri": "http://localhost:30151",
"noRooms": 100
}
}
You can access data from the IConfigurationRoot as following:
Configuration["AppSettings:uri"]
Like suggested in the comment I would put the information in a seperate class for that info and pass it into the DI container.
the class
public class AppSettings {
public string Uri { get; set; }
}
DI
public void ConfigureServices(IServiceCollection services)
{
services.Configure<AppSettings>(new AppSettings() { Uri = Configuration["AppSettings:uri"] });
// ...
}
Controller
public class DemoController
{
public HomeController(IOptions<AppSettings> settings)
{
//do something with it
}
}

Categories

Resources