Injection in a referenced class library? - c#

I have a ASP.NET Core 2.1 website that references a class library(DAL). To access the connectionstring from the appsettings.json(ASP.NET project) I need inject the configuration somehow. I have created a class in the class Library that looks like this :
public class Helper
{
IConfiguration Configuration;
public Helper(IConfiguration configuration)
{
Configuration = configuration;
}
public string GetConnectionString(string name)
{
return Configuration.GetConnectionString("DefaultConnection");
}
}
The injection pattern do however not pick this up so it demands a IConfigration to create the class.
How do I access the appsettings.json from the class library?

Your class library should not know or care how you're handling configuration in your app. All your Helper class needs is a connection string, so that is what you should inject into it. How that string is provided is an implementation detail that's part of your application domain.
public class Helper
{
public Helper(string connectionString)
{
// do something with connectionString
}
}
Then, in your app:
services.AddScoped(p =>
new Helper(Configuration.GetConnectionString("DefaultConnection")));

Related

How to read appsettings.json of parent projetc inside a Nuget Package project?

I'm creating an application that will be a Nuget Package. Let's say the name of this application is MyPackage.
I'll install this package in another project. Let's say this project's name is MyProject.
How can I read in the MyPackage project the appsettings.json of the MyProject project?
I can think of a couple options off the top of my head. Both assume you're participating in Dependency Injection and that by "appsettings.json" you actually mean the configuration framework.
One option is to specify IConfiguration as a dependency. This gives you access to all of the configuration keys. Assuming you're using constructor injection, it would look something like this:
public class MyPackageClass
{
public MyPackageClass(IConfiguration configuration)
{
// use configuration here
}
}
A better option is to export a configuration class from your package, bind that class to configuration in your dependency registration code, and inject it into your package classes using IOptions or IOptionsMonitor.
Let's say your configuration class looks like this:
public class MyPackageSettings
{
public string FirstSetting { get; set; }
}
Your registration code would look something like this:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddMyPackage(this IServiceCollection services, IConfiguration configuraiton)
{
services.Configure<MyPackageSettings>(configuration.GetSection("MyPackage");
// add other package classes to services
return services;
}
}
And you would consume the configuration in your package classes like this:
public class MyPackageClass
{
public MyPackageClass(IOptions<MyPackageSettings> options)
{
string firstSettingValue = options.Value.FirstSetting;
}
}
Your documentation would need to specify that users must provide the configuration in their appsettings.json (or other configuration source). For appsettings.json it would need to look something like this:
{
"MyPackage": {
"FirstSetting": "one"
}
}
More about using options: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-5.0

ASP.NET Core DI in a class library?

I have a ASP.NET Core 2.1 project that references a "Data Access Layer" project of typ .NET Core Class Library.
The Data Access Layger needs connection string from the appsettings.json in the ASP.NET Core project.
I have created a simple container like this :
public class DatabaseConnectionString : IDatabaseConnectionString
{
private readonly string _connectionString;
public DatabaseConnectionString(string connectionString)
{
_connectionString = connectionString;
}
public string ConnectionString {
get { return _connectionString; }
set { }
}
}
In the ASP.NET Core Startup.cs > ConfigureService I have this :
services.AddScoped<IDatabaseConnectionString>(p => new DatabaseConnectionString(Configuration.GetConnectionString("DefaultConnection")));
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container. But How do I get it while in the class library? I dont want to pass it all the way down from the controller and just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
I probably need a service where I can ask to create a object of a class and let the service fill in the constructor interfaces with the correct objects?
For example filling in the IDatabasConnectionString in this class :
public class UserFactory : FactoryBase
{
private readonly IDatabaseConnectionString _iDatabaseConnectionString;
public UserFactory(IDatabaseConnectionString connectionString)
{
_iDatabaseConnectionString = connectionString;
}
}
I know that I can add the IDatabaseConnectionString to a constructor of a controller in ASP.NET to get the container.
No, that's not needed and it would be wrong.
just adding the IDatabaseConnectionString to the constructor of a class in the class library do not work.
It doesn't work because you need to create the service that will use the connection string and add it to the services container.
For example:
public class Repository: IRepository
{
public Repository(IDatabaseConnectionString databaseConnectionString)
{
_databaseConnectionString = databaseConnectionString;
}
}
public class ServiceThatRequiresDatabase : IServiceThatRequiresDatabase
{
public ServiceThatRequiresDatabase(IRepository repository)
{
_repository = repository;
}
}
// ...
services.AddScoped<IRepository, Repository>();
services.AddScoped<IServiceThatRequiresDatabase, ServiceThatRequiresDatabase>();
public class HomeController : Controller
{
public HomeController(IServiceThatRequiresDatabase service)
{
_service = service;
}
}
By the way, as #YeldarKurmangaliyev said, your DatabaseConnectionString should be like this if you want to make it read-only:
public class DatabaseConnectionString : IDatabaseConnectionString
{
public string ConnectionString { get; }
public DatabaseConnectionString(string connectionString)
{
ConnectionString = connectionString;
}
}
There is no difference between controller and class from a class library. You need to
Define a class in a class library and inject IDatabaseConnectionString into it. Your UserFactory is the right way.
register the UserFactory for DI
serviceCollection.AddScoped<IUserFactory, UserFactory>();
Resolve the UserFactory by the DI. For example, use the UserFactory as the constructor parameter in some controller. Everything is connected by DI automatically.
public MyController(IUserFactory userFactory)
{
_userFactory = myUserFactory;
}
Here is the good explanation for understanding Composition root.

How to inject connection string into external assembly (project) controller?

My Web API is using other project for one controller. Service works fine. But I am struggling to inject connection string from main Web API project into controller in external project.
How could this be achieved?
public class MyExternalController : Controller
{
private string _connStr;
public MyExternalController(string connStr)
{
_connStr = connStr;
}
// actions here
}
As others said in the comments, for something like a controller, you should be injecting something concrete like a DbContext, not a connection string. However, for future reference your issue here is injecting a string. There's no way to register something in the DI container to satisfy a dependency like that. Instead, you should inject your configuration or a strongly-typed configuration class.
Injecting IConfigurationRoot is a bit of an anti-pattern, but for something like a connection string, it's fine:
public MyExternalController(IConfigurationRoot config)
{
_connStr = config.GetConnectionString("MyConnectionString");
}
For everything else, though, you should use strongly-typed configuration classes.
public class FooConfig
{
public string Bar { get; set; }
}
Then, in ConfigureServices:
services.Configure<FooConfig>(Configuration.GetSection("Foo"));
Which of course would correspond with some bit of config like:
{
"Foo": {
"Bar": "Baz"
}
}
Then, in your controller, for example:
public MyExternalController(IOptionsSnapshot<FooConfig> fooConfig)
{
_fooConfig = fooConfig.Value;
}

Dependency injection : static parameters and services

I have this service that will be injected to some other controllers.
It needs a service, and a connection string that is taken from a configuration file.
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, string connectionString){
//...
}
}
I would like IService1 and IService2 to be injected, but connectionString to be specified manually. I can't get my head around a way to work this out, the examples I saw were either massively complex, or just not what I wanted to achieve.
public void ConfigureServices(IServiceCollection services)
{
var cfg = new MyConfiguration();
Configuration.Bind("config", cfg);
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService, MyService>(/*what can I do here?*/)
}
Can this be achieved simply ?
You have access to the service provider within the factory delegate.
Resolve the other dependencies and inject the static variable when initializing the service.
//...
var connectionString = cfg["myConnectionString"];
services.AddSingleton<IMyService>(_ =>
new MyService(_.GetService<IService1>(), _.GetService<IService2>(), connectionString));
In the above example _ in the factory delegate is a IServiceProvider, and GetService<T> extension method is used to resolve the other services provided they are also registered with the service collection.
The factory delegate will be invoked the first time IMyService is requested.
As an alternative, referencing Options pattern in ASP.NET Core
And assuming for example a settings.json file
{
"myConnectionString": "value1_from_json",
}
consider using the IOptions<T> provided by the configuration extension.
create a class to store the desired configuration.
public class MyConnections {
public string MyConnectionString { get; set; }
}
refactor the class to depend on IOption<MyConnections>
public class MyService : IMyService {
private string connectionString;
public MyService(IService1 service1, IService2 service2, IOptions<MyConnections> options){
connectionString = options.Value.MyConnectionString;
//...
}
//...
}
and configure it on start up
//...
services.Configure<MyConnections>(Configuration);
services.AddSingleton<IMyService, MyService>();
You could do it like Nkosi suggests and use a factory that creates the service. This however has the drawback that this is very explicit and will require you to always adjust the object creation whenever your constructor signature changes (for example when you need different dependencies).
A more proper solution in the ASP.NET Core world would be to use the options pattern. What you do is basically instead of requiring a connection string to be passed to the constructor, you create a new configuration type that configures your service. You can then configure this object using the options pattern.
This would look like this:
public class MyService : IMyService
{
public MyService(IService1 service1, IService2 service2, IOptions<MyServiceOptions> serviceOptions)
{
var connectionString = serviceOptions.Value.ConnectionString;
//...
}
}
public class MyServiceOptions
{
public string ConnectionString
{ get; set; }
}
And then in your startup, just register your type and configure the options:
services.AddSingleton<IMyService, MyService>();
services.Configure<MyServiceOptions>(options => {
options.ConnectionString = "connection string";
});
If you place the configuration for your connection string into a separate configuration section, you could even do this:
services.Configure<MyServiceOption>(configuration.GetSection("MyService"));
Assuming your configuration looks like this:
{
"MyService": {
"ConnectionString": "…"
},
// …
}

Passing in a new connection string for a test database

I have a asp.net project with a DAL (most code is from someone else but I'm doing maintenance and clean up).
My web application project has a dataprovider class like so:
public class DataProvider : IDataProvider
{
private string DefaultConnectionString
{
get
{
return ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["myConnection"]].ConnectionString;
}
}
public T ExecuteMyQuery<T>(parameters)
{
//executes query on db
}
This web application project as has a data layer that interacts between the web pages and the provider like so:
public class MyData : IMyData
{
private readonly IDataProvider dataProvider;
public MyData(IDataProvider dataProvider)
{
this.dataProvider = dataProvider;
}
public string GetTitle(string myVar)
{
//builds up stuff to send
return this.dataProvider.ExecuteMyQuery(...);
}
}
The connection string for this project is in web.config.
Now I have my tests. For simplicity and time constraints I'm using just a test database and will create the actual objects and test against the test database (in theory we would use more mocks, stubs and all sorts of stuff but no time.)
So I have a test project which references my web application project above.
public class MyTests
{
private MyData myData;
private DataProvider dataProvider;
[SetUp]
protected void SetUp()
{
dataProvider = new DataProvider();
mysData = new MyData(dataProvider);
}
[Test]
public void CheckTitles()
{
string title = myData.GetTitle("AVariable");
Assert.AreEqual(title, "A typical title");
}
But this keeps give me a null pointer execption when I run it in NUnit. How do I get my provider to use a new connection string to point to my test database? I tried added an app.config file but it's not working.
It seems like your app.config (which is copy of web.config) is not loaded as configuration file by default when tests are being run.
I'd prefere to solve that in the following way:
Create IConfiguration interface which encapsulates details of getting connectionString
public interface IConfiguration {
string MyConnectionString { get; }
}
Create implementation of IConfiguration which reads web.config to use it in real code)
public class Configuration : IConfiguration
{
public string MyConnectionString
{
get { return ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["myConnection"]].ConnectionString; }
}
}
Introduce IConfiguration dependency into DataProvider class
public class DataProvider : IDataProvider
{
private IConfiguration configuration;
public DataProvider(IConfiguration configuration)
{
this.configuration = configuration;
}
private string DefaultConnectionString
{
get
{
return configuration.MyConnectionString;
}
}
...
}
Simple refactoring extracts knowledge about reading web.config from your DataProvider :)
In your test use IConfiguration stub which returns any connection string what you need for testing. (you can create stub by using mocking framework like moq/rhino-mock or by implementation of IConfiguration which does not read app.config)
public class MyTests
{
private MyData myData;
private DataProvider dataProvider;
private IConfiguration configuration;
[SetUp]
protected void SetUp()
{
// e.g. here stub is created via Moq
var configurationMock = new Mock<IConfiguration>();
configurationMock.SetupGet(x => x.MyConnectionString).Returns("test connection string");
configuration = configurationMock.Object;
dataProvider = new DataProvider(configuration);
mysData = new MyData(dataProvider);
}
...
}
If reading web.config is not goal of your tests then that approach is what you need.
As a bonus knowledge about configuration reading goes to separated class :)

Categories

Resources