Create shared class from IHttpContextAccessor to pass it across class libraries - c#

I`m using .NET Core 3.1 and I for every request I need to access some of the logged User claims and pass it to business service (another class library) outside the API project.
I`ve tried to create UserService:
public class UsersService
{
private readonly IHttpContextAccessor _context;
public UsersService(IHttpContextAccessor context)
{
_context = context;
}
public UserSession GetUser()
{
return new UserSession()
{
UserDisplayName = _context.HttpContext.User?.Identity?.Name,
};
}
}
and in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddHttpContextAccessor();
which should be used/injected by other business services or repositories like that:
public class ExampleBusinessService
{
private readonly IUnitOfWork _unitOfWork;
private IUsersService _userService;
public ExampleBusinessService(IUnitOfWork unitOfWork, IUsersService userService)
{
this._unitOfWork = unitOfWork;
this._userService = userService;
}
However it seems I cannot access IHttpContextAccessor outside of the Web project.
Can this be achieved?
Ofcourse I cannot create the UserService in the API and then use it in other libraries cause that mens the libraries should have reference to the web project.
If not is it possible in controller for the startup class to take the needed info from current user and pass it to the services? Or do so in the controller somehow?
As is say i need custom class with information taken from the logged user in business services (seprate class library) and repositories (seprate class library)
this is how the solution looks like i need to access the logged user info (custom UserSession class) in BusinessServices library:
Thanks

The IHttpContextAccessor type is part of the ASP.NET Core framework and as such only available in your web project. If you have your UsersService implementation in a different project, then you won’t be able to reference the context accessor correctly.
A good way to solve this is to split the contract from the implementation: Define a IUsersService within your library project and only reference that for your business logic. Users of the service do not need to know what things an implementation might depend on; they only care that they can use the public interface of this service.
Then in your web project, create an implementation of IUsersService that uses the IHttpContextAccessor to access the user information. Since the implementation lives within the web project, it can properly access the context accessor.
Finally, combine the interface and the implementation by registering your service implementation as part of your web application:
services.AddTransient<IUsersService, UsersService>();
Once your services (outside of the web project) now require the IUsersService, they will get the implementation that uses the context accessor to access the user information and everything should work without you having to add a reference to the ASP.NET Core framework in your (otherwise unrelated) libraries.

Okay, I understand your concern and it seems you're approaching the problem in the wrong way.
To point it out, I believe this is what you want:
You have ExampleBusinessService, which resides in a shared class library
You have UsersService, which deals with services of user sessions etc.
You want to access session info through IHttpContextAccessor
First of all, both ExampleBusinessService and UsersService don't need to be in API project or tied to it whatsoever. You can freely put them in your class library.
Here is how you can approach this:
Firstly, in your class library add Microsoft.AspNetCore.Http package.
Create static class UsersService (in the class library)
Add the following code (to UsersService)
public static class UsersService
{
// extend IHttpContextAccessor
public static UserSession GetUserSession( this IHttpContextAccessor accessor)
{
return new UserSession
{
UserDisplayName = _context.HttpContext.User?.Identity?.Name,
}
}
}
Now, in your ExampleBusinessService inject IHttpContextAccessor
public class ExampleBusinessService
{
private readonly IHttpContextAccessor _contextAccessor;
public ExampleBusinessService(IHttpContextAccessor accessor)
{
_contextAccessor = accessor,
}
// now do whatever you want with the session info
public SomeBusinessAboutSessionInfo()
{
// retrieve the session
var userSession = _contextAccessor.GetUserSession();
//...your logic
}
}
The final piece (this is important)
Add IHttpContextAccessor to your IServiceCollection in the API project
services.AddTransient<IHttpContextAccessor, HttpContextAccessor>();
Everything should now work out!!!

In case of adding full dotnetcore to a shared solution (external class library project). Just add a reference to Microsoft.AspNetCore.HTTP to get access to IHttpContextAccessor

Related

How do I use dll of .net 5 class library that contain in its class Dependency injection?

In my Asp.net Core 5 API Project
I have a serviceLayer that the controller uses, to get data from a third layer called dataLayer.
I want to use the service layer as a DLL in different projects.
This ServiceLayer Contain dependency Injections like that :
namespace ServiceLayer
{
public class UserService : IUserService
{
IUserRepository userRepository; // (From DataLayer)
public UserService(IUserRepository repository) : base(repository)
{
this.userRepository = repository;
}
public Users GetAllPersonsById(int id)
{
return userRepository.GetById(id);
}
}
public interface IUserService : IService<Users>
{
Users GetAllPersonsById(int id);
}
How can I use the method GetAllPersonsById with the DLL ServiceLayer
can I use it because the dependency Injections
As soon as you reference the DLL / project you can use all classes the same ways as if they were in the project.
To use a class as a service:
Provide the service
Inject the service
There's a lot of documentation available, so I'll keep this short:
// provide in startup.cs
services.AddTransient<IUserService, UserService>();
// Inject where you need it
MyConstructor(IUserService userService) {}
See https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0
Provide Extension Method
If we take a look at other libs, most of them provide a method to setup the services.
Example: Entity framework core
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDbContext>(options => options.UseSqlServer(...));
}
So you could:
In your lib, create an extension method for IServicesCollection that adds all services of your lib.
In the consuming project, call services.AddMyLibServices().
This could look like so:
public static class ServicesConfiguration
{
public static void AddDataLayer(this IServiceCollection services)
{
services.AddTransient<IUserService, UserService>();
// ... same for all services of your lib
}
}
Here's a tutorial with more details:
https://dotnetcoretutorials.com/2017/01/24/servicecollection-extension-pattern/
Lamar service registries
An optional and alternative approach are service registries. It's very similar to the extension methods but uses a class to do the setup. See https://jasperfx.github.io/lamar/documentation/ioc/registration/registry-dsl/
Composition Root
You may want to read about the composition root pattern, e.g. What is a composition root in the context of dependency injection?
In a simple app, your startup.cs is your composition root. In more complex apps, you could create a separate project to have a single place to configure your apps services.
Create the DLL
There are two ways to create the DLL:
As a project in your solution (so your solution has multiple projects, each will result in a separate DLL)
As a separate solution and as nuget package

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.

Dependency injection resolving by name

How can I inject different implementation of object for a specific class?
For example, in Unity, I can define two implementations of IRepository
container.RegisterType<IRepository, TestSuiteRepositor("TestSuiteRepository");
container.RegisterType<IRepository, BaseRepository>();
and call the needed implementation
public BaselineManager([Dependency("TestSuiteRepository")]IRepository repository)
As #Tseng pointed, there is no built-in solution for named binding. However using factory method may be helpful for your case. Example should be something like below:
Create a repository resolver:
public interface IRepositoryResolver
{
IRepository GetRepositoryByName(string name);
}
public class RepositoryResolver : IRepositoryResolver
{
private readonly IServiceProvider _serviceProvider;
public RepositoryResolver(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public IRepository GetRepositoryByName(string name)
{
if(name == "TestSuiteRepository")
return _serviceProvider.GetService<TestSuiteRepositor>();
//... other condition
else
return _serviceProvider.GetService<BaseRepositor>();
}
}
Register needed services in ConfigureServices.cs
services.AddSingleton<IRepositoryResolver, RepositoryResolver>();
services.AddTransient<TestSuiteRepository>();
services.AddTransient<BaseRepository>();
Finally use it in any class:
public class BaselineManager
{
private readonly IRepository _repository;
public BaselineManager(IRepositoryResolver repositoryResolver)
{
_repository = repositoryResolver.GetRepositoryByName("TestSuiteRepository");
}
}
In addition to #adem-caglin answer I'd like to post here some reusable code I've created for name-based registrations.
UPDATE Now it's available as nuget package.
In order to register your services you'll need to add following code to your Startup class:
services.AddTransient<ServiceA>();
services.AddTransient<ServiceB>();
services.AddTransient<ServiceC>();
services.AddByName<IService>()
.Add<ServiceA>("key1")
.Add<ServiceB>("key2")
.Add<ServiceC>("key3")
.Build();
Then you can use it via IServiceByNameFactory interface:
public AccountController(IServiceByNameFactory<IService> factory) {
_service = factory.GetByName("key2");
}
Or you can use factory registration to keep the client code clean (which I prefer)
_container.AddScoped<AccountController>(s => new AccountController(s.GetByName<IService>("key2")));
Full code of the extension is in github.
You can't with the built-in ASP.NET Core IoC container.
This is by design. The built-in container is intentionally kept simple and easily extensible, so you can plug third-party containers in if you need more features.
You have to use a third-party container to do this, like Autofac (see docs).
public BaselineManager([WithKey("TestSuiteRepository")]IRepository repository)
After having read the official documentation for dependency injection, I don't think you can do it in this way.
But the question I have is: do you need these two implementations at the same time? Because if you don't, you can create multiple environments through environment variables and have specific functionality in the Startup class based on the current environment, or even create multiple Startup{EnvironmentName} classes.
When an ASP.NET Core application starts, the Startup class is used to bootstrap the application, load its configuration settings, etc. (learn more about ASP.NET startup). However, if a class exists named Startup{EnvironmentName} (for example StartupDevelopment), and the ASPNETCORE_ENVIRONMENT environment variable matches that name, then that Startup class is used instead. Thus, you could configure Startup for development, but have a separate StartupProduction that would be used when the app is run in production. Or vice versa.
I also wrote an article about injecting dependencies from a JSON file so you don't have to recompile the entire application every time you want to switch between implementations. Basically, you keep a JSON array with services like this:
"services": [
{
"serviceType": "ITest",
"implementationType": "Test",
"lifetime": "Transient"
}
]
Then you can modify the desired implementation in this file and not have to recompile or change environment variables.
Hope this helps!
First up, this is probably still a bad idea. What you're trying to achieve is a separation between how the dependencies are used and how they are defined. But you want to work with the dependency injection framework, instead of against it. Avoiding the poor discover-ability of the service locator anti-pattern. Why not use generics in a similar way to ILogger<T> / IOptions<T>?
public BaselineManager(RepositoryMapping<BaselineManager> repository){
_repository = repository.Repository;
}
public class RepositoryMapping<T>{
private IServiceProvider _provider;
private Type _implementationType;
public RepositoryMapping(IServiceProvider provider, Type implementationType){
_provider = provider;
_implementationType = implementationType;
}
public IRepository Repository => (IRepository)_provider.GetService(_implementationType);
}
public static IServiceCollection MapRepository<T,R>(this IServiceCollection services) where R : IRepository =>
services.AddTransient(p => new RepositoryMapping<T>(p, typeof(R)));
services.AddScoped<BaselineManager>();
services.MapRepository<BaselineManager, BaseRepository>();
Since .net core 3, a validation error should be raised if you have failed to define a mapping.

How to get Microsoft.AspNet.Http.HttpContext instance in Class Constructor using DI

I am building a throwaway application in MVC 6 and experimenting with different architectures for dependencies.
The problem I am facing is how to create a custom 'MyAppContext' object specific to the Application. This would require some information from the HttpContext and some information from the database, and will be a request-scoped repository for application specific attributes. I want to pass the instance of the HttpContext into the constructor of the 'MyAppContext'.
I have successfully created a 'DataService' object with an IDataService interface using DI and this works Ok.
The difference with the 'MyAppContext' class is that it has two parameters in the constructor - the 'DataService' and the Microsoft.AspNet.Http.HttpContext. Here is the MyAppContext class:
public class MyAppContext : IMyAppContext
{
public MyAppContext(IDataService dataService, HttpContext httpContext)
{
//do stuff here with the httpContext
}
}
In the startup code, I register the DataService instance and the MyAppContext instance:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
//adds a singleton instance of the DataService using DI
services.AddSingleton<IDataService, DataService>();
services.AddScoped<IMyAppContext, MyAppContext>();
}
public void Configure(IApplicationBuilder app)
{
app.UseErrorPage();
app.UseRequestServices();
app.UseMvc(routes => /* routes stuff */);
}
I am expecting the HttpContext parameter in the constructor to get resolved by DI.
When running the code, this is the exception I get returned:
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNet.Http.HttpContext' while attempting to activate 'MyAppContext'
I figure this is because there is no specific instance of HttpContext that this error is occurring, but I don't know how to register the HttpContext instance in DI. I added the line 'app.UseRequestServices();' but this hasn't made any difference. I also tried a variant of:
services.AddScoped<HttpContext, HttpContext>();
But this fails because the second HttpContext is supposed to be an instance - I know it's not correct but haven't been able to work out what is.
So, in summary - how can I pass in the HttpContext object into the constructor of MyAppContext?
Inject IHttpContextAccessor in the constructor
By injecting an HttpContext into your component you are violating the SOLID principles. To be more specifically, you are violating:
The Dependency Inversion Principle (DIP) because you depend on a framework type (the HttpContext).
The Interface Segregation Principle (ISP) because the HttpContext has many methods, while the consumer never uses them all.
Both violations make it much harder to test your code. Although you can instead inject the IHttpContextAccessor as #victor suggests, this is still a violation of both the DIP and ISP, because this is an abstraction that is provided by the framework and you still depend on HttpContext. According to the DIP it is the client who should define the abstraction. This causes your code to be needlessly coupled to the framework.
Instead you should strive to specify narrow role interfaces; interfaces that do one specific thing for you that is specific to the needs of your application. Injecting a big dictionary with string values (as what HttpContext is, is never very specific). From your question it's unclear what kind of data you need from our MyAppContext, but I expect something like information of the currently logged in user. For this you can define a specific IUserContext abstraction, for instance:
public interface IUserContext {
IPrincipal CurrentUser { get; }
}
An adapter that connects the application to the ASP.NET framework can be easily created for this abstraction:
sealed class AspNetUserContextAdapter : IUserContext {
private readonly IHttpContextAccessor accessor;
public AspNetUserContextAdapter(IHttpContextAccessor accessor) {
this.accessor = accessor;
}
public IPrincipal CurrentUser => accessor.HttpContext.User;
}
This adapter does depend on IHttpContextAccessor, but this is okay, since the adapter is an infrastructural component located in the Composition Root. There are serveral ways to register this class, for instance:
services.AddSingleton<IUserContext, AspNetUserContext>();
In the startup class:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
using Microsoft.Extensions.DependencyInjection;
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddMvcCore();
}
In the controller:
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Core;
private readonly IHttpContextAccessor _httpContextAccessor;
public ServerSentEventController(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
Why would you pass the HttpContext in the constructor?
Why not directly access it wherever you want?
public MyAppContext(IDataService dataService)
{
HttpContext mycontext = HttpContext.Current;
//do stuff here with mycontext
}

Categories