.NET Core - ILogger DI and Base Classes/Object Classes - c#

I must be missing something on DI in .NET Core and I'd need your help.
I've been using DI successfully for my Service Classes, my Azure Client classes etc etc, but I can't understand how to get that working with my base classes (classes that just represent a basic object).
Let's say I have a vary simple class like
public class Person{
private readonly Ilogger<Person> logger;
public string name {get; set;}
public string lastname {get; set;}
public void LogYourName(){
logger.logInformation(this.name);
}
}
Also, let's say I have a HTTP rest API or another service that return as result an instance of Person.
How can I initiate ILogger in Person class without using DI?

The Microsoft Dependency Injection framework requires you to implement via the Constructor. You would need to do:
public class Person
{
private readonly ILogger logger;
public Person(ILogger logger) => this.logger = logger;
// Additional Code Here
}
When the object is constructed the framework will inject the logger instance for you. However, based on your above question- to avoid the the Dependency Injection would need to do:
var logger = services.GetService<ILogger>();
var person = new Person(logger);
The services would derive from IServiceProvider. I would need to see how you instantiate the IServiceCollection and populate your IServiceProvider. That would allow you to manually put the interface into the class while still using the Microsoft Dependency Injection framework.

Related

access appsetting from data layer without creating instantiation with parameter

How can i access my "appsettings.js" values in Business/Data layer without creating instantiation in the constructor level.
I can access all values in the controller but i don't want to send to all my layers like passing constructor parameter.
I am trying to understand this post but i don't understand fully. can some one provide me some sample code with out creating instantiation
ASP.NET 5 DI app setting outside controller
Controller - Services - DBServices (without parameter constructor?)
appsetting.json
"DbSettings":{
"ConnectionString" : "TESTING Connection string here"
}
Controller:
public class TestController : ControllerBase
{
public TestController(IOptions<DbSettings> dbSettings)
{
_balService = new BALServices(dbSettings.Value); // passing parameter "dbsetting" to constructor which i don't want.
}
}
Startup.cs
services.Configure<DbSettings>(Configuration.GetSection("DbSettings"));
services.AddSingleton<IConfiguration>(Configuration);
Dbsettings.cs
public class DbSettings
{
public string ConnectionString { get; set; }
}
The framework isn’t really designed to allow you to do that. ASP.NET Core uses dependency injection and kind of forces you to follow it, since pretty much everything is inaccessible outside of the DI container. So whenever something depends on something, then it should make that dependency clear by expecting it in the constructor. That is the way it is designed.
That being said, you creating a new instance of BALServices isn’t really in the spirit of dependency injection either. If you want to use BALServices in the controller, then instead of creating an instance of it itself, the controller should just depend on that type.
So you would change the controller to look like this:
public class TestController : ControllerBase
{
private readonly BALServices _balService;
public TestController(BALServices balService)
{
_balService = balService;
}
}
Now, the controller does not need to worry about how it could possibly create that object. It just depends on it being there and letting someone else figure that out. And that’s exactly what dependency injection is about.
In order to make that work, of course you now need to adjust your BALServices to depend on those IOptions<DbSettings> instead:
public class BALServices
{
private readonly DbSettings _dbSettings;
public BALServices(IOptions<DbSettings> dbSettings)
{
_dbSettings = dbSettings.Value;
}
}
And then you only need to register this service in your Startup:
services.AddScoped<BALServices>();
And now you have TestController depend on the actual service it wants to use, and BALServices depend on the configuration it requires. And the DI container will automatically provide you with what you need.

How to use NLog in static class where everything else is wired with Autofac

My MVC app is wired with Autofac. I have also configured NLog which works as expected in my controller classes. My nLogger is registered as below:
var builder = new ContainerBuilder();
builder.RegisterGeneric(typeof(LoggerService<>))
.As(typeof(ILoggerService<>)).SingleInstance();
var container = builder.Build();
And the constructor of the ILoggerService is:
public LoggerService()
{
SourceClass = typeof (T).FullName;
Logger = LogManager.GetLogger(SourceClass);
}
Now I have also got many static helper classes that I use. For example:
public static class Helper
{
public static string GenerateQrBitmap(string secret, string issuer, string userEmail)
{
...
}
}
But I want to be able to use the logger in these Helper classes as well.
This is one of the reasons why static classes aren't great.
You have two options:
Make them not static (and register them with Autofac) and take
ILoggerService as a constructor parameter.
Change their methods
(e.g. GenerateQrBitmap) to take a ILoggerService as a parameter.
I'd suggest the former.
The alternative is to use the Service Locator pattern - and have Helper resolve directly against the container. I will not show you how to do this, since I don't recommend it. It makes the code harder to unit test, and it hides your dependencies. But if you Google Autofac Service Locator static class c# I'm sure you'll work it out.

.Net core Dependency injection - with parameters

NOTE: This example has been simplified
I have got a Client's Contact table and wanted to retrieve specific client contact information from DB. The code I typed belove brings me all contact details. I wanted to use a parameter to only bring me specific client contacts.
I used IClientContactRepository interface like this
public interface IClientContactRepository
{
IQueryable<ClientContactModel> ClientContacts { get; }
}
And i used this class to retrive data from database with dapper
public class ClientContactRepository : IClientContactRepository
{
private readonly IConfiguration configuration;
private List<ClientContactModel> ClientContactList {get;set;}
public ClientContactRepository(IConfiguration config)
{
configuration = config;
SqlConnection conn = new SqlConnection(configuration["ConnectionString"]);
using (var connection = conn)
{
ClientContactList = connection.Query<ClientContactModel>("Select * FROM ContactTable ").ToList();
}
}
public IQueryable<ClientContactModel> ClientContacts => ClientContactList;
}
In my Startup class
services.AddTransient<IClientContactRepository, ClientContactRepository>();
My QUESTION is: can I pass the client's id parameter to the constructor.
I tried this: add a parameter to the constructor
public ClientContactRepository(IConfiguration config, int clientId)
and tried to start up class.
services.AddTransient<IClientContactRepository, ClientContactRepository(int,i)>()
Didn't work....
Can someone help me how to pass parameter please?
Yes, but where are you getting the client ID from - is it a configured value that will be static for the lifetime of the application? If so, you can use the AddTansient method overload that accepts a factory delegate to create the objects.
The better way (will cover all use cases) is registering the type that can provide that information (create one if no such type exists) with the DI container and use that as a parameter in the constructor of your repo.
As an example, let’s say you get your client ID from a claim, so the type you need to inject is IPrincipal:
services.AddScoped<IPrincipal>(
provider => provider.GetService<IHttpContextAccessor>()
.HttpContext
.User);
You would then inject the IPrincipal into your repo constructor and retrieve the client ID. An even better way would be to create your own type “ClientIdAccessor” which is responsible for providing the client ID. You would then not have a dependency on IPrincipal when testing your repo and the implementation of this new type would only depend on external libraries for your asp.net core implementation.
Side note: are you certain you want to use AddTransient for your repo? Usually you’d want to use the same repo object for the lifetime of the request (I.e. AddScoped).

Is it possible to inject a specific configuration class bound as a suboption and not to IConfiguration<MyType>?

I have a class that I want injected into some controllers. It has a few dependencies that I want to be automatically injected. Specifically this class requires a configuration object be passed in.
So my appsettings.json file looks something like this:
{
"MyType": {
"Option1": "value"
}
}
And I have a class that mirrors this, ex.
public class MyTypeConfig
{
public string Option1 { get; set; }
}
I have registered this in my startup class.
services.Configure<MyTypeConfig>(Configuration.GetSection("MyType"));
However, I want to receive a "naked" version of MyTypeConfig in the class being instantiated, NOT IConfigureOptions<MyTypeConfig> which is what the DI system seems to want to inject.
The constructor for the class being injected looks liked this (which works):
public MySampleClass(IHttpClientFactory httpClient, IConfigureOptions<MyTypeConfig> configuration, ILogger<MySampleClass> logger)
However, this is really want I want:
public MySampleClass(IHttpClientFactory httpClient, MyTypeConfig configuration, ILogger<MySampleClass> logger)
I've looked through the topic on the Microsoft site, here:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/options?view=aspnetcore-2.2
But I'm not really finding an answer.
How can I just inject the MyTypeConfig?
Get the class from configuration during startup and register it with the service collection.
MyTypeConfig config = Configuration.GetSection("MyType").Get<MyTypeConfig>();
services.AddSingleton<MyTypeConfig>(config);
Reference Configuration in ASP.NET Core : Bind to an object graph
In 2022, imho, there is a better way to do that
Reference

How do I get access to INodeServices without dependency injection

I'd like to run some node code from my c#. Microsoft.AspNetCore.NodeServices seems to be the way to do this job however the examples are all very similar and all involve putting
services.AddNodeServices();
in the configure services function and then DI adds the implementation of INodeServices to a controller. Like this
public class foo
{
private readonly INodeServices _nodeServices;
public foo(INodeServices nodeServices)
{
_nodeServices = nodeServices;
}
}
As I'm using this in a class library rather than a webapi how is DI going to work? Also how do I call the class from a unit test, what can I pass into the constructor? I'm sure I'm missing something obvious.
The concept of DI is that it can be used to resolve object graphs. That is, it doesn't just resolve dependencies of the Controller class, but dependencies of those dependencies, dependencies of those dependencies, etc.
To use INodeServices in your own library, you simply need to reference Microsoft.AspNetCore.NodeServices, then accept it as a constructor parameter.
public class MyServiceFromMyLibrary : IMyServiceFromMyLibrary
{
private readonly INodeServices nodeServices;
public MyServiceFromMyLibrary(INodeServices nodeServices)
{
this.nodeServices = nodeServices;
}
// ...
}
Then reference your library from the Web API project and inject your service into a controller.
public class FooController
{
private readonly IMyServiceFromMyLibrary myService;
public FooController(IMyServiceFromMyLibrary myService)
{
this.myService = myService;
}
}
DI takes care of putting the INodeServices instance into your class provided it is registered in your composition root, as follows.
services.AddNodeServices();
services.AddTransient<IMyServiceFromMyLibrary, MyServiceFromMyLibrary>();
If your end game is to create a reusable library rather than an application layer refer to DI Friendly Library for some techniques to make your library easier to use without the use of dependency injection.
Also how do I call the class from a unit test, what can I pass into the constructor?
For a unit test, you would just need to mock INodeServices. The simplest way is to use a mocking library, such as Moq.
var mock = new Mock<INodeServices>();
mock.Setup(ns => ns.InvokeAsync(It.IsAny<string>())).Returns(...);
var target = new MyServiceFromMyLibrary(mock.Object);
// .. Call a method on target and then assert the results
References:
Using Node Services in ASP.NET Core
Dependency injection in ASP.NET Core

Categories

Resources