I have a library in a large asp.net 4.8 site that uses the ConfigurationManager deep down in the bowels of the project. At the time, no one had a problem with the dependency on that library. Well, we're upgrading to asp.net 5 (i.e. Core) which doesn't really use the ConfigurationManager anymore, instead uses a DI version of IConfguration. Unfortunately the access of the connection strings is so deep, passing along this object from the Controller down through the libraries isn't feasible. Is there any kind of "ConfigurationManager.ConnectionStrings" equivalent that reads the application.json file's ConnectionStrings section?
You can use the following Code
public class SampleClass
{
private readonly IConfiguration _config;
public SampleClass(IConfiguration config)
{
_config = config;
}
public string GetConnectingString(string name)
{
return _config.GetConnectionString(name);
}
}
Assuming you have a connectionstrings section in your appsettings.json, you can use the GetConnectionString() method on the IConfiguration.
connectionString = configuration.GetConnectionString("DefaultConnection");
https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.configuration.configurationextensions.getconnectionstring?view=dotnet-plat-ext-6.0
Related
I have the following project structure in my Azure function:
Application.Function
Application.Domain
Application.Infrastructure
This works fine. All the dependencies are resolved without any errors.
However, when I setup Entity Framework in my Infrastructure layer, and I'm trying to run the application, I get the following error:
webjobsbuilderextensions.cs not found
When I remove all the Entity Framework related things and try to run the application, it works again.
In my Startup.cs I have the following code snippet:
builder.Services.AddDomain(connectionString);
Application.Domain.Injections:
public static class Injections
{
public static IServiceCollection AddDomain(this IServiceCollection services, string connectionString)
{
services.AddTransient<ISalesItemService, SalesItemService>();
services.AddPersistence(connectionString)
return services;
}
}
Application.Infrastructure.Injections:
public static class Injections
{
public static IServiceCollection AddPersistence(this IServiceCollection services, string connectionString)
{
services.AddTransient<ISalesItemDataService, SalesItemDataService>();
services.AddDbContext<IOnePlmSubContext, OnePlmSubContext>(
options => options.UseSqlServer(connectionString),
ServiceLifetime.Transient,
ServiceLifetime.Transient);
return services;
}
}
Has anyone else experienced the same issue that I have? Can't I have this kind of layered structure when working with Azure functions?
Summarize the comments for other communities reference:
Just downgrade version of EF Core to 3.1 and re-build the function, then re-rest the funciton, it works.
I have a scenario where we have a "standardised startup" for many small AspNet Core websites.
A seemingly obvious solution to achieve this is to refactor the Startup.cs class into a separate common assembly (as Infrastructure.Web.Core.Startup). We then have each small AspNet Core website reference it the common assembly and use that startup class instead:
public static Microsoft.AspNetCore.Hosting.IWebHostBuilder CreateWebHostBuilder( string[] args )
{
return new WebHostBuilder()
.UseKestrel()
.ConfigureServices( collection => { } )
.UseContentRoot( System.IO.Directory.GetCurrentDirectory() )
.UseStartup<Infrastructure.Web.Core.Startup>(); //.UseStartup<Startup>();
}
Somehow, this breaks attribute routing in the sense that the routes are not hit. No errors, but not routing. The moment I copy the class back into the website project (with the exact same code) it works again.
As a test, if I wrap the Startup.cs class in the common library in a local startup class (like below), it also works:
public class Startup
{
private readonly Infrastructure.Web.Core.Startup _startup;
public Startup( IConfiguration configuration )
{
_startup = new Infrastructure.Web.Core.Startup( configuration );
}
public IConfiguration Configuration => _startup.Configuration;
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices( IServiceCollection services )
{
_startup.ConfigureServices( services );
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure( IApplicationBuilder app, IHostingEnvironment env )
{
_startup.Configure( app, env );
}
}
If I had to take a guess, it's probably something to do with the Dependency Injection.
Does anyone have any suggestions?
FYI: It's using typical AspNet Core 2.1 projects
UPDATE
Incidentally, if I use inheritance it also works but the derived class must be in the same project as the website. I guess it seems obvious, but thought I include that information for completeness sake:
public class Startup : Infrastructure.Web.Core.Startup
{
public Startup( IConfiguration configuration ) : base(configuration)
{
}
}
You can fix this by adding the following statement to your services in your Startup.cs method.
services.AddApplicationPart(typeof(AnTypeInTheOtherAssembly).Assembly);
This will tell the View/Controller Discovery to also check for the new location. Your Project which contains the Startup.cs file would be the Startup Project, and all the others would be just references and libraries or similar.
As of .Net Core 3 you can use something called Razor Class Libraries, see the MSDN. This will automatically add this your Controllers and Views to the discovery, it also has debugging support and will work just as a normal Class Library would.
I am having some problems for getting the configuration values using this method in my web application in .net core.
var settings = ConfigurationManager.GetSection("JwtBearerSettings/Issuer");
This method is called by one project I am referencing. It is also in .NET Core 2.2 and the config settings are on the file
appsettings.json
The thing is that when I am debugging always I get settings == null I have been looking for similar questions but I couldn't solve this.
My json is
{
"JwtBearerSettings": {
"Issuer": "Auth.MyStuff.Api",
"PrivateKey": "Blablabla",
"ExpirationTime": "15"
}
}
Do you know if is even possible to use this method?
Am I missing something between, for getting the web app configuration with my library?
Given that you've created a property like this:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
}
You can then access the elements, like this:
var issuer = Configuration["JwtBearerSettings:Issuer"],
The simplest way to read config out is to simply read out a value at a time. Structured data is separated by colons, so your example becomes:
var issuer = Configuration["JwtBearerSettings:Issuer"];
This is clearly a pain if you are reading a lot of settings. So you can instead simply Bind a model object to a section. For example, if you had a model object with properties in it, say
class JwtBearerSettings {
public string Issuer {get;set;}
}
then you can use GetSection to bind to a settings object:
var jbs = new JwtBearerSettings();
Configuration.GetSection("JwtBearerSettings").Bind(jbs);
This pattern is used by a lot of libraries as it lets you provide as little, or as much config as you need - as long as the model is pre-filled with sensible defaults.
The third pattern supported .Net Core can be used with DI:
Here you can register your configuration model with the service factory:
var jbs = Configuration.GetSection("JwtBearerSettings");
services.Configure<JwtBearerSettings>(jbs);
and, to reference your settings in a service, simply add IOptions as a ctor parameter:
public MyService(IOptions<JwtBearerSettings> jwtOptions)
{
Config = jwtOptions.Value;
}
It seems to me that it's a bad idea to have a domain service require an instance of IOptions<T> to pass it configuration. Now I've got to pull additional (unnecessary?) dependencies into the library. I've seen lots of examples of injecting IOptions all over the web, but I fail to see the added benefit of it.
Why not just inject that actual POCO into the service?
services.AddTransient<IConnectionResolver>(x =>
{
var appSettings = x.GetService<IOptions<AppSettings>>();
return new ConnectionResolver(appSettings.Value);
});
Or even use this mechanism:
AppSettings appSettings = new AppSettings();
Configuration.GetSection("AppSettings").Bind(appSettings);
services.AddTransient<IConnectionResolver>(x =>
{
return new ConnectionResolver(appSettings.SomeValue);
});
Usage of the settings:
public class MyConnectionResolver
{
// Why this?
public MyConnectionResolver(IOptions<AppSettings> appSettings)
{
...
}
// Why not this?
public MyConnectionResolver(AppSettings appSettings)
{
...
}
// Or this
public MyConnectionResolver(IAppSettings appSettings)
{
...
}
}
Why the additional dependencies? What does IOptions buy me instead of the old school way of injecting stuff?
Technically nothing prevents you from registering your POCO classes with ASP.NET Core's Dependency Injection or create a wrapper class and return the IOption<T>.Value from it.
But you will lose the advanced features of the Options package, namely to get them updated automatically when the source changes as you can see in the source here.
As you can see in that code example, if you register your options via services.Configure<AppSettings>(Configuration.GetSection("AppSettings")); it will read and bind the settings from appsettings.json into the model and additionally track it for changes. When appsettings.json is edited, and will rebind the model with the new values as seen here.
Of course you need to decide for yourself, if you want to leak a bit of infrastructure into your domain or pass on the extra features offered by the Microsoft.Extensions.Options package. It's a pretty small package which is not tied to ASP.NET Core, so it can be used independent of it.
The Microsoft.Extensions.Options package is small enough that it only contains abstractions and the concrete services.Configure overload which for IConfiguration (which is closer tied to how the configuration is obtained, command line, json, environment, azure key vault, etc.) is a separate package.
So all in all, its dependencies on "infrastructure" is pretty limited.
In order to avoid constructors pollution of IOptions<>:
With this two simple lines in startup.cs inside ConfigureServices you can inject the IOptions value like:
public void ConfigureServices(IServiceCollection services)
{
//...
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
services.AddScoped(cfg => cfg.GetService<IOptions<AppSettings>>().Value);
}
And then use with:
public MyService(AppSettings appSettings)
{
...
}
credit
While using IOption is the official way of doing things, I just can't seem to move past the fact that our external libraries shouldn't need to know anything about the DI container or the way it is implemented. IOption seems to violate this concept since we are now telling our class library something about the way the DI container will be injecting settings - we should just be injecting a POCO or interface defined by that class.
This annoyed me badly enough that I've written a utility to inject a POCO into my class library populated with values from an appSettings.json section. Add the following class to your application project:
public static class ConfigurationHelper
{
public static T GetObjectFromConfigSection<T>(
this IConfigurationRoot configurationRoot,
string configSection) where T : new()
{
var result = new T();
foreach (var propInfo in typeof(T).GetProperties())
{
var propertyType = propInfo.PropertyType;
if (propInfo?.CanWrite ?? false)
{
var value = Convert.ChangeType(configurationRoot.GetValue<string>($"{configSection}:{propInfo.Name}"), propInfo.PropertyType);
propInfo.SetValue(result, value, null);
}
}
return result;
}
}
There's probably some enhancements that could be made, but it worked well when I tested it with simple string and integer values. Here's an example of where I used this in the application project's Startup.cs -> ConfigureServices method for a settings class named DataStoreConfiguration and an appSettings.json section by the same name:
services.AddSingleton<DataStoreConfiguration>((_) =>
Configuration.GetObjectFromConfigSection<DataStoreConfiguration>("DataStoreConfiguration"));
The appSettings.json config looked something like the following:
{
"DataStoreConfiguration": {
"ConnectionString": "Server=Server-goes-here;Database=My-database-name;Trusted_Connection=True;MultipleActiveResultSets=true",
"MeaningOfLifeInt" : "42"
},
"AnotherSection" : {
"Prop1" : "etc."
}
}
The DataStoreConfiguration class was defined in my library project and looked like the following:
namespace MyLibrary.DataAccessors
{
public class DataStoreConfiguration
{
public string ConnectionString { get; set; }
public int MeaningOfLifeInt { get; set; }
}
}
With this application and libraries configuration, I was able to inject a concrete instance of DataStoreConfiguration directly into my library using constructor injection without the IOption wrapper:
using System.Data.SqlClient;
namespace MyLibrary.DataAccessors
{
public class DatabaseConnectionFactory : IDatabaseConnectionFactory
{
private readonly DataStoreConfiguration dataStoreConfiguration;
public DatabaseConnectionFactory(
DataStoreConfiguration dataStoreConfiguration)
{
// Here we inject a concrete instance of DataStoreConfiguration
// without the `IOption` wrapper.
this.dataStoreConfiguration = dataStoreConfiguration;
}
public SqlConnection NewConnection()
{
return new SqlConnection(dataStoreConfiguration.ConnectionString);
}
}
}
Decoupling is an important consideration for DI, so I'm not sure why Microsoft have funnelled users into coupling their class libraries to an external dependency like IOptions, no matter how trivial it seems or what benefits it supposedly provides. I would also suggest that some of the benefits of IOptions seem like over-engineering. For example, it allows me to dynamically change configuration and have the changes tracked - I've used three other DI containers which included this feature and I've never used it once... Meanwhile, I can virtually guarantee you that teams will want to inject POCO classes or interfaces into libraries for their settings to replace ConfigurationManager, and seasoned developers will not be happy about an extraneous wrapper interface. I hope a utility similar to what I have described here is included in future versions of ASP.NET Core OR that someone provides me with a convincing argument for why I'm wrong.
I can't stand the IOptions recommendation either. It's a crappy design to force this on developers. IOptions should be clearly documented as optional, oh the irony.
This is what I do for my configuraition values
var mySettings = new MySettings();
Configuration.GetSection("Key").Bind(mySettings);
services.AddTransient(p => new MyService(mySettings));
You retain strong typing and don't need need to use IOptions in your services/libraries.
You can do something like this:
services.AddTransient(
o => ConfigurationBinder.Get<AppSettings>(Configuration.GetSection("AppSettings")
);
Using Net.Core v.2.2, it's worked for me.
Or then, use IOption<T>.Value
It would look something like this
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
I would recommend avoiding it wherever possible. I used to really like IOptions back when I was working primarily with core but as soon as you're in a hybrid framework scenario it's enough to drive you spare.
I found a similar issue with ILogger - Code that should work across frameworks won't because I just can't get it to bind properly as the code is too dependent on the DI framework.
I have an ASP.NET 5 web application that has pulled in a .NET 4.6 class library. At some point in the class library there is a call to get a connection string from web.config:
ConfigurationManager.ConnectionStrings["AppDataConnectionString"].ConnectionString
(NOTE: I CAN NOT CHANGE THIS CODE.)
This class library has been used in old web forms applications, where the AppDataConnectionString was defined in their web.configs. Now I'm trying to use the class library in my ASP.NET 5 web app, but the above code throws a null reference exception.
Here is the connection strings section in my web.config in the ASP.NET 5 project:
<connectionStrings>
<add name="AppDataConnectionString" connectionString="server=xxxxxx;database=yyyyy;Trusted_Connection=yes;" providerName="System.Data.SqlClient" />
</connectionStrings>
I've also tried adding it in an appsettings.json file as follows:
{
"ConnectionStrings": {
"AppDataConnectionString": {
"ConnectionString": "server=xxxxx;database=yyyyy;Trusted_Connection=yes;"
}
}
}
Here is where I load the configuration in Startup:
public IConfiguration _Configuration { get; set; }
public Startup()
{
_Configuration = new ConfigurationBuilder().AddJsonFile("appsettings.json").Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(provider => _Configuration);
}
public void Configure(IApplicationBuilder app)
{
// Call some code from the class library that tries to get that connection string.
}
It seems to me like the configuration manager just can't find where the connection string is. Do I have my web.config or appsettings.json structured in the right way for it to find it?
It turns out that any configuration that was stored previously in a web.config file in previous versions of ASP.net now needs to be stored in an app.config file in the same directory as project.json in ASP.net 5.0. Now my imported class libraries can get their connections strings and everything works.