Understanding C# FromServices - c#

So iv just been thrown into some new code at work and its in C#. Now I'm not very familiar with C# and there are some things I really don't understand and the main one is Injecting into methods.
Now there is a WebAPI and it has controller that uses a class named "LocalFileStorage" which is a dependency from another project, the constructor for that class looks like this.
public class LocalFileStorageHandler : IStorageHandler
{
*Bunch of private variables
public LocalFileStorageHandler(DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger)
{ code here}
Now in the controller class every method that uses the LocalFileStorage gets it injected as a parameter. Here is a example:
public async Task<IActionResult> ApiMapper([FromBody] dynamic request, [FromServices] IStorageHandler storageHandler)
Also in the project startup.cs we can find this line:
services.AddScoped<IStorageHandler, LocalFileStorageHandler>();
Now my understanding is that for each separate request made the Addscoped makes sure that the method gets its own instance of LocalFileStorage handler. I also understand that the "[FromServices]" attribute causes this instance to be injected into the method. However the one thing I don't understand and cant find anywhere in the code is where the LocalFileStorage objects get their "In parameters" for their constructor?
As by my understanding each injected instance of LocalFileStorage should also receive the parameters:
DbContext dbContext, IConfiguration configuration, ILogger<LocalFileStorageHandler> logger
What am i missing here ?
Kind regards

The DI container injects the dependencies for you. So somewhere, the DbContext, IConfiguration and ILogger has been registered/setup.
Then when you use the FromServices attribute, the DI container will try to resolve the type for you and inject all dependencies (if they are registered, if not, an exception will be thrown)
IConfiguration and ILogger are usually setup when building the host. DbContext are (usually) registered by using the AddDbContext extension method.
Link to configuration for ILogger: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/logging/?view=aspnetcore-6.0#logging-providers
Link to configuration for IConfiguration: https://learn.microsoft.com/en-us/aspnet/core/fundamentals/configuration/?view=aspnetcore-6.0#default-configuration
Link to Dependency Injection fundamentals in ASP.Net Core : https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-6.0

Related

Is there a way for a simple way for ClassLibrary to get Session Value?

I have an external ClassLibrary Project that needs to get session value set from HomeController in the Main Project.
Is there a simple way to accomplish this?
Or is there an alternative to transfer a value from HomeController to an external ClassLibrary?
You can use the IHttpContextAccessor class
For other framework and custom components that require access to
HttpContext, the recommended approach is to register a dependency
using the built-in dependency injection container. The dependency
injection container supplies the IHttpContextAccessor to any classes
that declare it as a dependency in their constructors.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddHttpContextAccessor();
services.AddTransient<IUserRepository, UserRepository>();
}
In the following example:
UserRepository declares its dependency on IHttpContextAccessor.
The dependency is supplied when dependency injection resolves the dependency chain and creates an instance of UserRepository.
.
public class UserRepository : IUserRepository
{
private readonly IHttpContextAccessor _httpContextAccessor;
public UserRepository(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public void LogCurrentUser()
{
var username = _httpContextAccessor.HttpContext.User.Identity.Name;
service.LogAccessRequest(username);
}
}
Don't forget to add services.AddHttpContextAccessor(); to make the dependency injection work.
The single-responsibility principle dictates that a class should do just one thing. While you can inject something like IHttpContextAccessor that then requires the class to have knowledge of concepts like HttpContext, Session, the fact that it's being used in a web environment in the first place, etc.
The correct approach is to inject or pass values. If the class needs a particular value from a session variable, access the session in your controller, where that logic actually belongs, and then pass only the value from the session to your external class.
If u use Abp template, the Abp application service ApplicationService already contains property AbpSession, you can inherit this class.

Share Microsoft.Extensions.DependencyInjection.ServiceProvider configuration between projects

I have a solution that has the following projects
Acme.Core
Acme.Domain
Acme.Repositories
Acme.Services
Acme.Web
In the past I've used Unity for DI in full framework projects. I was able to register concrete objects to interface mappings in executable projects (web apps, console app, test apps).
I'm trying to implement the same approach with .NET Core. I wanted to first try using the Microsoft.Extensions.DependencyInjection library. Within the ASP.NET Core application it works great. Unfortunately I've run into an issue when I try to share/reference that instance with the registions to other projects, such as a .NET Standard library.
My idea was to inject the ServiceProvider into the constructor of the service:
public class AddressService : BaseService, IAddressService
{
private readonly IServiceProvider _serviceProvider;
public AddressService(IServiceProvider serviceProvider, string userOrProcessName)
{
_serviceProvider = serviceProvider;
}
public IReadOnlyList<IState> GetAllStates()
{
_serviceProvider.GetService<IAddressRepository>();
// other logic removed
}
}
I tried the following inside the Startup.ConfigureServices():
services.AddTransient<IAddressService>(s => new AddressService(HttpContext.RequestServices, Environment.UserName));
The issue I ran into is that I cannot reference HttpContext.RequestServices outside of a Controller. I haven't been able to figure another way of passing the ServiceProvider instance.
My questions:
How do pass a reference for the current ServiceProvider?
Is there a better design to accomplish my goal sharing the configuration of Microsoft.Extensions.DependencyInjection in multiple libraries?
Prevent injecting IServiceProvider into your application components; that leads to the Service Locator anti-pattern.
Instead, you should build up application components solely using Constructor Injection. This means that your AddressService should require IAddressRepository as constructor argument, not IServiceProvider. For instance:
public class AddressService : IAddressService
{
private readonly IAddressRepository repo;
public AddressService(IAddressRepository repo, IUserContext userContext)
{
this.repo = repo;
}
public IReadOnlyList<IState> GetAllStates()
{
// other logic removed
}
}
Also try to prevent injecting primites into your constructors. It's not a bad practice per se, but it does complicate object graph construction. Instead, either wrap the value into a class, in case its a configuration value, or hide it behind an abstraction (as shown above) in case it's a runtime value.
Both practices simplify both your application code and the Composition Root.
For instance, this will be the result of the previous AddressService redesign:
services.AddTransient<IAddressRepository, SqlAddressRepository>();
services.AddTransient<IAddressService, AddressService>();
services.AddScoped<IUserContext, UserContext>();
services.AddHttpContextAccessor();
Here, UserContext could be defined as follows:
public class UserContext : IUserContext
{
private readonly IHttpContextAccessor accessor;
public UserContext(IHttpContextAccessor accessor) => this.accessor = accessor;
public string UserName => this.accessor.HttpContext.User.Identity.Name;
}
In order to share configuration across multiple projects, you can put the configuration into a shared assembly, and register (not resolve) them in there. Many dependency injection libraries offer that functionality. e.g.
in Autofac you create a module (https://autofaccn.readthedocs.io/en/latest/configuration/modules.html) that takes a container builder to configure:
protected override void Load(ContainerBuilder builder) { ... }
SimpleInjector provides packages: https://simpleinjector.readthedocs.io/en/latest/howto.html#package-registrations
Unity can support something similar: Can I register my types in modules in Unity like I can in Autofac?
Ninject has a similar module feature: What is the intention of Ninject modules?
A similar feature has be created for Microsoft.Extensions.DependencyInjection: https://github.com/aruss/DotNetCore_ModularApplication
At a high level, you create a method that receives the DI container and adds your registrations to that container. If your DI framework doesn't provide hooks you need to manually call the method yourself, but the general concept doesn't change.
Splitting registrations into modules allows you to easily group similar sets of functionality while maintaining the flexibility of incorporating different sets of functionality into different projects. You could of course create a single shared assembly that registered the union of all dependencies for all projects, but that would carry around unnecessary baggage and result in a less reusable implementation.
The key point as Steven points out is that you configure the container and let it inject the dependencies rather than looking from the inside out for the dependencies.

ASP.NET Core MVC Dependency Injection via property or setter method

It has been well documented, how to inject dependencies into services.
Question: But is it (already) possible in ASP.NET Core 2.0 to have the system's DI mechanism automatically inject a service into a method or into a property?
Sidenote: In PHP-Symfony this pattern is called setter injection.
Example:
Say I have a common MyBaseController class for all controllers in my project and I want a service (e.g. the UserManager service) to be injected into MyBaseController that can be later accessed in all child controllers. I could use constructor injection to inject the service in the child class and pass it via base(userManager) to the parent. But having to perform this in all child constructors of all controllers is pretty tedious.
So I would like to have a setter in MyBaseController like this:
public abstract class MyBaseController : Controller
{
public UserManager<User> userManager { get; set; }
// system should auto inject UserManager here
public void setUserManager(UserManager<User> userManager) {
this.userManager = userManager;
}
}
...so I don't have to do the following in every child constructor just to pass the dependency to the parent:
public class UsersController : MyBaseController
{
public ChildController(UserManager<User> userManager) : base(userManager) {}
Update: The answer given here is what I want to achieve, however the question was asked for ASP.NET Core 1.0, I'm interested in whether any solutions have been added in ASP.NET Core 2.0.
In general it is good advice to avoid non constructor DI as it is considered a bit of an anti pattern, there is a good discussion about it in this related question.
With the default Microsoft.Extensions.DependencyInjection container in aspnet core, the answer is no, but you could swap to something more powerfull like autofac (which has property injection) if you are sure you really need this feature.
You can perform setter injection with the built-in DI container (Microsoft.Extensions.DependencyInjection) using Quickwire. Unlike Autofac, this is not a new DI container, it just extends the default one.
To make it work:
Add this to ConfigureServices
public void ConfigureServices(IServiceCollection services)
{
// Activate controllers using the dependency injection container
services.AddControllers().AddControllersAsServices();
services.ScanCurrentAssembly();
// ...
// Register your other services
}
By default, ASP.NET Core will activate controllers by instantiating them directly without going through the DI container. Fortunately, this behaviour can easily be overridden to force ASP.NET Core to resolve controllers using dependency injection. This is what services.AddControllers().AddControllersAsServices() does.
ScanCurrentAssembly is necessary to get Quickwire to search for services declared in your assembly and register them (which will include your controllers).
Decorate your child controller with [RegisterService]
[RegisterService(ServiceLifetime.Transient)]
public class ChildController : MyBaseController
{
// ...
}
This will make your ChildController discoverable when ScanCurrentAssembly is called in step 1.
Decorate the setter
public abstract class MyBaseController : Controller
{
[InjectService]
public UserManager<User> UserManager { get; private set; }
}
Now the UserManager property in your child controller will be automatically set from the dependency injection container.
You have two kind of DI
Mandatory, it's injection needed for object initialization then it's injection setted in constructor.
Optional, it's injection needed for action.
If DI is doing well, u can have unit test without injection system, if all injections are in ctor then you'll break every unit test, every time for nothing.
So all injections in ctor break open/close principle.
One more point is DI is for interface implementation or module public part, object under this implementation are initialized manually.
So setter is not bad because there are hidden by interface.

Using Repository method in controller (ASP.NET Core)

I use repository method to get all data from DB.
Here is the code of it:
public class ExperienceRepository
{
private readonly ToSeeDatabaseContext _context;
public ExperienceRepository(ToSeeDatabaseContext context)
{
_context = context;
}
public List<Experience> GetAllExperiences()
{
return _context.Experience.ToList();
}
}
I need to call GetAllExperience from controller.
So at first I need to declare repo as private property
I do it like this
private ExperienceRepository _exprepo = new ExperienceRepository();
But it says, it need
Severity Code Description Project File Line Suppression State
Error CS7036 There is no argument given that corresponds to the required formal parameter 'context' of 'ExperienceRepository.ExperienceRepository(ToSeeDatabaseContext)' TooSeeWeb C:\Users\EugeneSukhomlyn\source\Workspaces\TooSee\Too See Web\Too See Web\Too See Web\Controllers\ExperienceController.cs 14 Active
How I can solve it?
Since you are using dependency injection, the preferred way would be to inject the DB context to the repository.
You probably have already code similar to this in the ConfigureServices method of your Startup.cs file (or in the place where you configure your service collection if you are not using ASP.NET Core) to set up the context for dependency injection (if you don't you should add it):
services.AddDbContext<ToSeeDatabaseContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("ToSeeDatabaseContext")));
Since your experience repository already accepts ToSeeDatabaseContext, it is already ready for dependency injection. Now you have to inform the DI framework about ExperienceRepository, so that it can inject it to its consumers. So in ConfigureServices you add:
services.AddTransient<ExperienceRepository, ExperienceRepository>();
Now can use dependency injection whenever you want to need the repository. In your consumer (eg. an ASP.NET page) you can use constructor injection to get a repository:
class MyExperienceConsumer {
private ExperienceRepository _exprepo;
public MyExperienceConsumer(ExperienceRepository exprepo) {
_exprepo = exprepo;
}
}
If your consumer is an ASP.NET page controller, this is all you need to do, since the MVC framework will create the controller for you and use DI to give you the repository. If you need to instantiate the consumer yourself you need to do so with the help a service provider from the DI framework, so that it can do its magic (assuming you have a service collection). When you use ActivatorUtilities, the DI framework will inject the repository into the constructor:
IServiceProvider serviceProvider = serviceCollection.BuildServiceProvider();
MyExperienceConsumer consumer =
ActivatorUtilities.CreateInstance<MyExperienceConsumer>(serviceProvider);
In any case, you can use the DI framework to do the heavy lifting for you.
Your ExperienceRepository class have one constructor that requires a ToSeeDatabaseContext as parameter.
You are trying to create a instance ExperienceRepository with no parameters. The compiler can't find a constructor which doesn't take any parameters, producing the compiler error.

How do I inject my own classes into controllers in ASP.NET MVC Core?

I created my own class for user management called UserManager. I want controllers to have access to a UserManager object to sign in or register users.
I understand that I have to provide a parameterized constructor in the controller class that takes an object of UserManager and assigns it to a private attribute etc.
But where and how do I register my class in my project so that it will be injected automatically by the ASP.NET MVC Core framework?
In the ConfigureServices(IServiceCollection services) method in the Startup.cs class add:
services.AddTransient<IOperationTransient, Operation>();
services.AddScoped<IOperationScoped, Operation>();
services.AddSingleton<IOperationSingleton, Operation>();
Swapping out IOperationTransient, IOperationScoped or IOperationSingleton for your own class / services that need injected.
There are three ways to register services for injection:
Transient - These services will be created each time they are requested.
Scoped - These services will be created once per request.
Singleton - These services are created once on the first request and then every subsequent request will receive the same instance.
Classes that are injected with dependencies usually don't use the concrete classes (UserManager.cs) for their constructor parameters, but rely on interfaces e.g. IUserManager. Although it is possible to use concrete classes, an interface provides looser coupling which is the reason for using dependency injection in the first place.
Whenever the framework encounters a constructor (in this case the constructor of the controller) that "wants" an interface it has too look up which concrete class it should use for injection.
Where
The relationship between "class wants type X" and "framework will inject it with type Y" is defined in the ConfigureServices method in Startup.cs.
What
1. Decide on the lifetime of the created object
There are three options for defining the above relationship, that differ in the lifetime the object created by the dependency injection framework is alive.
The official documentation says:
Transient
Transient lifetime services are created each time they are requested.
This lifetime works best for lightweight, stateless services.
Scoped
Scoped lifetime services are created once per request.
Singleton
Singleton lifetime services are created the first time they are
requested (or when ConfigureServices is run if you specify an instance
there) and then every subsequent request will use the same instance.
2. Add the code
After choosing a lifetime (scoped in the following examples) you add the line
services.AddScoped<IInterfaceUsedByControllerParameter, ClassThatWillBeInjected>();
or for OPs class
services.AddScoped<IUserManager, UserManager>();
or if UserManager implements several interfaces:
services.AddScoped<ILogin, UserManager>();
services.AddScoped<IRegister, UserManager>();
With those two lines whenever a class requires a constructor parameter with either of those two interfaces the dependency injection will provide it with an object of type UserManager.
Code examples
In case of the last example where UserManager implements the interfaces ILogin and IRegister:
Controler
public class UserController : Controller
{
private readonly ILogin _login;
private readonly IRegister _registration;
public UserController(ILogin login, IRegister registration)
{
_login = login;
_registration = registration;
}
...
...
}
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
...
services.AddScoped<ILogin, UserManager>();
services.AddScoped<IRegister, UserManager>();
...
services.AddMvc();
...

Categories