I am using Autofac in my project and for the most part it works fine. A while ago, I needed to get access to the current user and was told the best way to do this was to create a wrapper class like this:
public class PrincipalProvider : IPrincipalProvider
{
public IPrincipal User => HttpContext.Current?.User;
}
This has worked without any issues through my application.
I now have a new provider which looks like this:
public class WmsProvider : IWmsProvider
{
private readonly Lazy<ILogProvider> _logProvider;
private readonly Lazy<IMessageProvider> _messageProvider;
private readonly CormarConfig _config;
private readonly ClaimsIdentity _identity;
public WmsProvider(IPrincipalProvider principalProvider, Lazy<ILogProvider> logProvider, Lazy<IMessageProvider> messageProvider)
{
_messageProvider = messageProvider;
_logProvider = logProvider;
_identity = (ClaimsIdentity)principalProvider.User.Identity;
}
/// <summary>
/// Sends the order to WMS
/// </summary>
/// <param name="model">The order model</param>
public async Task SendAsync(OrderViewModel model)
{
var request = WmsFactory.Create(model);
await _logProvider.Value.TraceAsync($"This is a test", _identity);
await _messageProvider.Value.CreateAsync(request, model.OrderNumber, MessageType.Wms, "ORD", Method.POST, null);
}
}
(I have stripped out the rest of the code for brevity)
In this case, the User is null and throws an error (Object instance not found).
But I have another class with a similar constructor:
public OrderProvider(CormarConfig config, IOrderService orderSerivce, IPrincipalProvider principalProvider, Lazy<IAccountProvider> accountProvider, Lazy<ICollectionManagerProvider> collectionManagerProvider, Lazy<IEmailProvider> emailProvider, Lazy<IJournalProvider> journalProvider, Lazy<IOrderLineProvider> orderLineProvider, Lazy<IStockProvider> stockProvider, Lazy<webServices> webService, Lazy<ITroposOrderLineService> troposOrderLineService, Lazy<ITroposOrderService> troposOrderService, Lazy<ITroposUnitOfWork> troposUnitOfWork, Lazy<IWmsProvider> wmsProvider)
{
//Assign our config
_config = config;
// Add our services to our class
_connectionType = config.ConnectionType;
_orderService = orderSerivce;
// Add our providers to our class
_identity = (ClaimsIdentity)principalProvider.User.Identity;
// Add our optional providers
_accountProvider = accountProvider;
_collectionManagerProvider = collectionManagerProvider;
_emailProvider = emailProvider;
_journalProvider = journalProvider;
_orderLineProvider = orderLineProvider;
_stockProvider = stockProvider;
_webService = webService;
_wmsProvider = wmsProvider;
_troposOrderLineService = troposOrderLineService;
_troposOrderService = troposOrderService;
_troposUnitOfWork = troposUnitOfWork;
}
And this works fine.
Both are registered the same way in my Module:
builder.RegisterType<OrderProvider>().As<IOrderProvider>().InstancePerRequest();
builder.RegisterType<WmsProvider>().As<IWmsProvider>().InstancePerRequest();
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>();
One thing to note, is that WmsProvider is injected into OrderProvider, so it is not directly injected into the controller. The controller constructor looks like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
This might be where the issue lies. In a dependent, is the context not available? If it isn't what is the solution? Is there a way to get to the context from the child?
Any help would be appreciated.
I figured this out. It was as I said, the context was not available in the nested classes, so to fix this I changed the registration of the PrincipalProvider to instance per request:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
And I changed the PrincipalProvider to look like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And this fixed my issue.
Related
I'm thinking that perhaps my entire idea of how to approach this is wrong, so let me explain what I'm trying to do.
I have a UserId that is a property contained within my JWT token.
On many of my REST endpoints, I need to read that UserId to use it within my DB queries.
I implemented a filter which intercepts all of my calls and decodes my JWT and assigns the UserId value into a static Globals class that I had created.
I just realised now though, that that class is GLOBAL. As in, the values are actually shared across the entire server for anybodies REST requests.
I intended for the value to essentially just be transiently available for the duration of each individual request.
How can I change my implementation so that I can globally access the UserId contained in the JWT token for the current request.
My suggestion is to make some kind of abstraction e.g ICurrentUser and make an implementation, which will take UserId from HttpContext.
// Define in Domain/Application project
public interface ICurrentUser
{
public string? Id { get; set; }
}
// Implement in ASP.NET project
public class CurrentUser : ICurrentUser
{
public CurrentUser(IHttpContextAccessor contextAccessor)
{
var user = contextAccessor.HttpContext?.User;
if (user == null)
{
return;
}
Id = user.FindFirstValue(ClaimTypes.NameIdentifier) ?? user.FindFirstValue(JwtClaimTypes.Subject);
}
public string? Id { get; set; }
}
Also, don't forget to add .AddHttpContextAccessor() call for you services
If you want something to be available for the duration of an individual request I would recommend using a service registered as scoped see Scoped Services
But lets start from the beginning. First implement a service itself like:
public UserService : IUserService
{
private readonly IHttpContextAccessor _accessor;
/// inject the `IHttpContextAccessor` to access the actual
/// request / token / headers etc.
public UserService(IHttpContextAccessor accessor)
{
_accessor = accessor;
}
public async Task<string> GetUserIdAsync()
{
var userId = await GetUserIdFromTokenAsync();
return userId;
}
private Task<string> GetUserIdFromTokenAsync()
{
/// Add your logic here to get or parse the
/// user id from the token or do some other stuff to get the user id.
/// ... or get the user id from the current User object claim
/// depends on your auth settings `_accessor.HttpContext?.User`
var token = _accessor... // from headers?
return userId;
}
}
/// Always use an interface to make it well testable and mockable for unit tests
public interface IUserService
{
Task<string> GetUserIdAsync();
}
Then in your dependency injection part (Startup.cs or Program.cs depends which tempate you have selected).
/// register the `IHttpContextAccessor` to be able to inject it.
services.AddHttpContextAccessor();
/// register your `UserService` as scoped!
services.AddScoped<IUserService, UserService>();
Now you can use this in all your services and controllers (which are at least also registered as scoped). This will resolve the service per request.
/// In a data service
class YourDataService
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public YourDataService(IUserService service)
{
_userService = service
}
public async Task DoYourQueryStuffAsync()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
}
}
/// The same applies for a controller
[ApiController]
[Route("values")]
public class ValuesController : ControllerBase
{
private readonly IUserService _userService;
/// Inject the `IUserService` wherever you need it now to
/// receive the current user Id.
public ValuesController(IUserService service)
{
_userService = service
}
[Authorized]
[HttpGet]
public async Task<IActionResult> Query()
{
var userId = await _userService.GetUserIdAsync();
/// Your application logic with the provided userId
///
var queryresult = await ...
return Ok(queryresult);
}
}
Notes at the end:
Do not fall into the trap to consume scoped services from a singleton service this is not working because singletons are persistent without the request context.
Documentation links:
ASP.net Core Dependency Injection
UserId in Bearer
I have web app written in ASP.NET Core v2.1. The app uses localized views configured via LocalizationOptions.ResourcesPath = "Resources" and the access to localized strings is via injected IViewLocalizer in cshtml files.
In some conditions I'd like to render the view with the different resource file than the default one located in Resources folder. The other resource file have the same keys as the default one (no need to change the view) so only the different texts will be rendered.
E.g. consider such action method in controller and see the comments what I'd like to solve:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
// how to render the view which will use different resource file
// then the default found in Resources folder?
// return View("MyView");
}
// this renders the view with the default resource file found in Resources folder
return View("MyView");
}
Firstly I'm not so sure about the necessity of your requirement. But by digging into the source code, I've found it possible and come up with a solution here.
Actually the IViewLocalizer is instantiated by IHtmlLocalizerFactory which is registered as a singleton. That depends on IStringLocalizerFactory which is also registered as a singleton. Here we use localization by resources file (managed by ResourceManager) so the implementation class is ResourceManagerStringLocalizerFactory.
That factory consumes the options LocalizationOptions to get the configured ResourcesPath used for creating the instance of IStringLocalizer which is wrapped by HtmlLocalizer and finally wrapped in ViewLocalizer. The point here is the result is cached by a cache key depending on the view/page path & the name of the assembly (in which the resources are embedded). So right after first time creating the instance of ViewLocalizer (available via DI), it will be cached and you have no chance to change the configured ResourcesPath or intercept to change it somehow.
That means we need a custom ResourceManagerStringLocalizerFactory to override the Create method (actually it's not virtual, but we can re-implement it). We need to include one more factor (the runtime resources path) to the cache key so that caching will work correctly. Also there is one virtual method in ResourceManagerStringLocalizerFactory that can be overridden to provide your runtime resources path: GetResourceLocationAttribute. To minimize the implementation code for the custom ResourceManagerStringLocalizerFactory, I choose that method to override. By reading the source code, you can see that it's not the only point to intercept to provide your own runtime resources path but it seems to be the easiest.
That's the core principle. However when it comes to the implementation for the full solution, it's not that simple. Here's the full code:
/// <summary>
/// A ViewLocalizer that can be aware of the request feature IActiveViewLocalizerFeature to use instead of
/// basing on the default implementation of ViewLocalizer
/// </summary>
public class ActiveLocalizerAwareViewLocalizer : ViewLocalizer
{
readonly IHttpContextAccessor _httpContextAccessor;
public ActiveLocalizerAwareViewLocalizer(IHtmlLocalizerFactory localizerFactory, IHostingEnvironment hostingEnvironment,
IHttpContextAccessor httpContextAccessor) : base(localizerFactory, hostingEnvironment)
{
_httpContextAccessor = httpContextAccessor;
}
public override LocalizedHtmlString this[string key, params object[] arguments]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key, arguments] : localizer[key, arguments];
}
}
public override LocalizedHtmlString this[string key]
{
get
{
var localizer = _getActiveLocalizer();
return localizer == null ? base[key] : localizer[key];
}
}
IHtmlLocalizer _getActiveLocalizer()
{
return _httpContextAccessor.HttpContext.Features.Get<IActiveViewLocalizerFeature>()?.ViewLocalizer;
}
}
public static class HtmlLocalizerFactoryWithRuntimeResourcesPathExtensions
{
public static T WithResourcesPath<T>(this T factory, string resourcesPath) where T : IHtmlLocalizerFactory
{
if (factory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(resourcesPath);
}
return factory;
}
}
public interface IActiveViewLocalizerFeature
{
IHtmlLocalizer ViewLocalizer { get; }
}
public class ActiveViewLocalizerFeature : IActiveViewLocalizerFeature
{
public ActiveViewLocalizerFeature(IHtmlLocalizer viewLocalizer)
{
ViewLocalizer = viewLocalizer;
}
public IHtmlLocalizer ViewLocalizer { get; }
}
public interface IRuntimeResourcesPath
{
string ResourcesPath { get; }
void SetRuntimeResourcesPath(string resourcesPath);
}
public class RuntimeResourcesPathHtmlLocalizerFactory : HtmlLocalizerFactory, IRuntimeResourcesPath
{
readonly IStringLocalizerFactory _stringLocalizerFactory;
public RuntimeResourcesPathHtmlLocalizerFactory(IStringLocalizerFactory localizerFactory) : base(localizerFactory)
{
_stringLocalizerFactory = localizerFactory;
}
//NOTE: the factory is registered as a singleton, so we need this to manage different resource paths used on different tasks
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
void IRuntimeResourcesPath.SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
public override IHtmlLocalizer Create(string baseName, string location)
{
if (_stringLocalizerFactory is IRuntimeResourcesPath overridableFactory)
{
overridableFactory.SetRuntimeResourcesPath(ResourcesPath);
}
return base.Create(baseName, location);
}
}
public static class RuntimeResourcesPathHtmlLocalizerFactoryExtensions
{
/// <summary>
/// Creates an IHtmlLocalizer with a runtime resources path (instead of using the configured ResourcesPath)
/// </summary>
public static IHtmlLocalizer CreateWithResourcesPath(this IHtmlLocalizerFactory factory, string resourcesPath, string baseName, string location = null)
{
location = location ?? Assembly.GetEntryAssembly().GetName().Name;
var result = factory.WithResourcesPath(resourcesPath).Create(baseName, location);
factory.WithResourcesPath(null);
return result;
}
}
public static class RuntimeResourcesPathLocalizationExtensions
{
static IHtmlLocalizer _useLocalizer(ActionContext actionContext, string resourcesPath, string viewPath)
{
var factory = actionContext.HttpContext.RequestServices.GetRequiredService<IHtmlLocalizerFactory>();
viewPath = viewPath.Substring(0, viewPath.Length - Path.GetExtension(viewPath).Length).TrimStart('/', '\\')
.Replace("/", ".").Replace("\\", ".");
var location = Assembly.GetEntryAssembly().GetName().Name;
var localizer = factory.CreateWithResourcesPath(resourcesPath, viewPath, location);
actionContext.HttpContext.Features.Set<IActiveViewLocalizerFeature>(new ActiveViewLocalizerFeature(localizer));
return localizer;
}
/// <summary>
/// Can be used inside Controller
/// </summary>
public static IHtmlLocalizer UseLocalizer(this ActionContext actionContext, string resourcesPath, string viewOrPageName = null)
{
//find the view before getting the path
var razorViewEngine = actionContext.HttpContext.RequestServices.GetRequiredService<IRazorViewEngine>();
if (actionContext is ControllerContext cc)
{
viewOrPageName = viewOrPageName ?? cc.ActionDescriptor.ActionName;
var viewResult = razorViewEngine.FindView(actionContext, viewOrPageName, false);
return _useLocalizer(actionContext, resourcesPath, viewResult.View.Path);
}
var pageResult = razorViewEngine.FindPage(actionContext, viewOrPageName);
//NOTE: here we have pageResult.Page is an IRazorPage but we don't use that to call UseLocalizer
//because that IRazorPage instance has very less info (lacking ViewContext, PageContext ...)
//The only precious info we have here is the Page.Path
return _useLocalizer(actionContext, resourcesPath, pageResult.Page.Path);
}
/// <summary>
/// Can be used inside Razor View or Razor Page
/// </summary>
public static IHtmlLocalizer UseLocalizer(this IRazorPage razorPage, string resourcesPath)
{
var path = razorPage.ViewContext.ExecutingFilePath;
if (string.IsNullOrEmpty(path))
{
path = razorPage.ViewContext.View.Path;
}
if (path == null) return null;
return _useLocalizer(razorPage.ViewContext, resourcesPath, path);
}
/// <summary>
/// Can be used inside PageModel
/// </summary>
public static IHtmlLocalizer UseLocalizer(this PageModel pageModel, string resourcesPath)
{
return pageModel.PageContext.UseLocalizer(resourcesPath, pageModel.RouteData.Values["page"]?.ToString()?.TrimStart('/'));
}
}
The custom ResourceManagerStringLocalizerFactory I've mentioned at the beginning:
public class RuntimeResourcesPathResourceManagerStringLocalizerFactory
: ResourceManagerStringLocalizerFactory, IRuntimeResourcesPath, IStringLocalizerFactory
{
readonly AsyncLocal<string> _asyncResourcePath = new AsyncLocal<string>();
public string ResourcesPath => _asyncResourcePath.Value;
private readonly ConcurrentDictionary<string, ResourceManagerStringLocalizer> _localizerCache =
new ConcurrentDictionary<string, ResourceManagerStringLocalizer>();
public RuntimeResourcesPathResourceManagerStringLocalizerFactory(IOptions<LocalizationOptions> localizationOptions, ILoggerFactory loggerFactory) : base(localizationOptions, loggerFactory)
{
}
protected override ResourceLocationAttribute GetResourceLocationAttribute(Assembly assembly)
{
//we is where we override the configured ResourcesPath and use the runtime ResourcesPath.
return ResourcesPath == null ? base.GetResourceLocationAttribute(assembly) : new ResourceLocationAttribute(ResourcesPath);
}
public void SetRuntimeResourcesPath(string resourcesPath)
{
_asyncResourcePath.Value = resourcesPath;
}
/// <summary>
/// Almost cloned from the source code of ResourceManagerStringLocalizerFactory
/// We need to re-implement this because the framework code caches the result of Create using a cache key depending on only baseName & location.
/// But here we introduce one more parameter of (runtime) ResourcesPath, so we need to include that in the cache key as well for
/// it to work properly (otherwise each time changing the runtime ResourcesPath, the same cached result will be returned, which is wrong).
/// </summary>
IStringLocalizer IStringLocalizerFactory.Create(string baseName, string location)
{
if (baseName == null)
{
throw new ArgumentNullException(nameof(baseName));
}
if (location == null)
{
throw new ArgumentNullException(nameof(location));
}
return _localizerCache.GetOrAdd($"B={baseName},L={location},R={ResourcesPath}", _ =>
{
var assemblyName = new AssemblyName(location);
var assembly = Assembly.Load(assemblyName);
baseName = GetResourcePrefix(baseName, location);
return CreateResourceManagerStringLocalizer(assembly, baseName);
});
}
}
One more extension class to help register the custom services conveniently:
public static class RuntimeResourcesPathLocalizationServiceCollectionExtensions
{
public static IServiceCollection AddRuntimeResourcesPathForLocalization(this IServiceCollection services)
{
services.AddSingleton<IStringLocalizerFactory, RuntimeResourcesPathResourceManagerStringLocalizerFactory>();
services.AddSingleton<IHtmlLocalizerFactory, RuntimeResourcesPathHtmlLocalizerFactory>();
return services.AddSingleton<IViewLocalizer, ActiveLocalizerAwareViewLocalizer>();
}
}
We implement a custom IViewLocalizer as well so that it can be seamlessly used in your code. Its job is just to check if there is any active instance of IHtmlLocalizer shared via the HttpContext (as a feature called IActiveViewLocalizerFeature. Each different runtime resources path will create a different IHtmlLocalizer that will be shared as the active localizer. Usually in one request scope (and in the view context), we usually just need to use one runtime resources path (specified at the very beginning before rendering the view).
To register the custom services:
services.AddRuntimeResourcesPathForLocalization();
To use the localizer with runtime resources path:
public async Task<IActionResult> ShowSomething([FromQuery] bool useDifferentResource)
{
if (useDifferentResource)
{
this.UseLocalizer("resources path of your choice");
}
return View("MyView");
}
NOTE: The UseLocalizer inside the Controller's or PageModel's scope is not very efficient due to an extra logic to find the view/page (using IRazorViewEngine as you can see in the code). So if possible, you should move the UseLocalizer to the RazorPage or View instead. The switching condition can be passed via view-model or any other ways (view data, view bag, ...).
I have a lot of services that require access to the current user.
I set up a PrincipalProvider which bound to the current HttpContext, I also registered it per request so that once instantiated, it would never lose the context.
It looks like this:
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly HttpContext _current;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider()
{
_current = HttpContext.Current;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _current?.User;
}
And I have bound it like this:
builder.RegisterType<PrincipalProvider>().As<IPrincipalProvider>().InstancePerRequest();
Now, any service that has this injected into it should have the user exposed. The issue I have is simple. If I have a controller set up like this:
public OrdersController(IOrderProvider provider)
{
_provider = provider;
}
That IOrderProvider has the IPrincipleProvider lazily injected into it, so in this case the user is available because the parent service is created on the Controller. The issue is if it isn't injected into the service, but instead used on another service that is injected into it.
For example, I have this controller:
public UsersController(IUserProvider provider, IAdvancedEncryptionStandardProvider encyptionProvider, IPhotoManager photoManager)
{
_userProvider = provider;
_encryptionProvider = encyptionProvider;
_photoManager = photoManager;
}
The IUserProvider doesn't have the IPrincipalProvider injected into it, but it does have another service that does:
public TroposSessionRequest(CormarConfig config, IAdvancedEncryptionStandardProvider encryptionProvider, Lazy<IPrincipalProvider> principalProvider)
{
_config = config;
_encryptionProvider = encryptionProvider;
_principalProvider = principalProvider;
}
The problem here, is that when the TroposSessionRequest is created, the HttpContext is not available and therefor is null.
I was hoping there was a way to instantiate the IPrincipleProvider when the context becomes available and then keep it for the entire request.
We can do this for every request as the PrincipalProvider is almost always needed.
Does anyone know how to do this?
The simplest way to workaround the problem is to simply access HttpContext.Current in your User accessor:
public IPrincipal User => HttpContext.Current?.User;
If you're concerned about testability, you can mock HttpContext.Current.User relatively easily.
If you're set on solving it using DI you could abstract HttpContext behind a IHttpContextAccessor dependency (but since you're still depending on the HttpContext type you'd still be in the same boat in terms of testing).
public class PrincipalProvider : IPrincipalProvider
{
// Readonly fields
private readonly IHttpContextAccessor _httpContextAccessor;
/// <summary>
/// Default constructor
/// </summary>
public PrincipalProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
/// <summary>
/// Gets the current user
/// </summary>
public IPrincipal User => _httpContextAccessor.HttpContext?.User;
}
Creating Dependency Injection with ASP.NET Core is fairly easy. The documentation explains it very well here and this guy has a killer video to explain it.
However, I want to do the same thing with my ASP.NET MVC 5 project. How can handle dependency injection with ASP.MVC 5?
Also, is Dependency injection limited to controllers only or can it work with any class?
In ASP.Net MVC you can use the .Net Core DI from NuGet rather than one of the third-party alternatives:-
using Microsoft.Extensions.DependencyInjection
For the MVC Start/Configuration class:-
public void Configuration(IAppBuilder app)
{
// We will use Dependency Injection for all controllers and other classes, so we'll need a service collection
var services = new ServiceCollection();
// configure all of the services required for DI
ConfigureServices(services);
// Configure authentication
ConfigureAuth(app);
// Create a new resolver from our own default implementation
var resolver = new DefaultDependencyResolver(services.BuildServiceProvider());
// Set the application resolver to our default resolver. This comes from "System.Web.Mvc"
//Other services may be added elsewhere through time
DependencyResolver.SetResolver(resolver);
}
My project uses Identity User and I've replaced the OWIN start-up configuration to follow a service-based approach instead. The default Identity User classes use static factory methods to create instances. I've moved that code into the constructors and relied on DI to provide the appropriate injection. It is still work in progress but here is where I am at:-
public void ConfigureServices(IServiceCollection services)
{
//====================================================
// Create the DB context for the IDENTITY database
//====================================================
// Add a database context - this can be instantiated with no parameters
services.AddTransient(typeof(ApplicationDbContext));
//====================================================
// ApplicationUserManager
//====================================================
// instantiation requires the following instance of the Identity database
services.AddTransient(typeof(IUserStore<ApplicationUser>), p => new UserStore<ApplicationUser>(new ApplicationDbContext()));
// with the above defined, we can add the user manager class as a type
services.AddTransient(typeof(ApplicationUserManager));
//====================================================
// ApplicationSignInManager
//====================================================
// instantiation requires two parameters, [ApplicationUserManager] (defined above) and [IAuthenticationManager]
services.AddTransient(typeof(Microsoft.Owin.Security.IAuthenticationManager), p => new OwinContext().Authentication);
services.AddTransient(typeof(ApplicationSignInManager));
//====================================================
// ApplicationRoleManager
//====================================================
// Maps the rolemanager of identity role to the concrete role manager type
services.AddTransient<RoleManager<IdentityRole>, ApplicationRoleManager>();
// Maps the role store role to the implemented type
services.AddTransient<IRoleStore<IdentityRole, string>, RoleStore<IdentityRole>>();
services.AddTransient(typeof(ApplicationRoleManager));
//====================================================
// Add all controllers as services
//====================================================
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
}
The Account Controller class has the single constructor:-
[Authorize]
public class AccountController : Controller
{
private ApplicationSignInManager _signInManager;
private ApplicationUserManager _userManager;
private RoleManager<IdentityRole> _roleManager;
public AccountController(ApplicationUserManager userManager, ApplicationSignInManager signInManager, RoleManager<IdentityRole> roleManager)
{
UserManager = userManager;
SignInManager = signInManager;
RoleManager = roleManager;
}
}
My Default Dependency Resolver:
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
For this answer I downloaded a Microsoft Example of WebApi project as a basis for the example and added DI services to it as follows,
Update the Target Framework to 4.6.1
NuGet the DI package :- Microsoft.Extensions.DependencyInjection
After the standard MapHttpRoute configuration, add code to register which services you need
using's
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http;
using Microsoft.Extensions.DependencyInjection;
using System.Web.Http.Dependencies;
using ProductsApp.Controllers;
WebApiConfig
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
// create the DI services and make the default resolver
var services = new ServiceCollection();
services.AddTransient(typeof(DefaultProduct));
services.AddTransient(typeof(ProductsController));
var resolver = new MyDependencyResolver(services.BuildServiceProvider());
config.DependencyResolver = resolver;
}
}
DefaultProduct
public class DefaultProduct : ProductsApp.Models.Product
{
public DefaultProduct()
{
this.Category = "Computing";
this.Id = 999;
this.Name = "Direct Injection";
this.Price = 99.99M;
}
}
MyDependencyResolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods.
/// This is combined dependency resolver for MVC and WebAPI usage.
/// </summary>
public class MyDependencyResolver : System.Web.Mvc.IDependencyResolver, System.Web.Http.Dependencies.IDependencyResolver
{
protected IServiceProvider serviceProvider;
protected IServiceScope scope = null;
public MyDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
public MyDependencyResolver(IServiceScope scope)
{
this.scope = scope;
this.serviceProvider = scope.ServiceProvider;
}
public IDependencyScope BeginScope()
{
return new MyDependencyResolver(serviceProvider.CreateScope());
}
public void Dispose()
{
Dispose(true);
}
protected virtual void Dispose(bool disposing)
{
scope?.Dispose();
}
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
ServiceProviderExtensions
public static class ServiceProviderExtensions
{
public static IServiceCollection AddControllersAsServices(this IServiceCollection services, IEnumerable<Type> serviceTypes)
{
foreach (var type in serviceTypes)
{
services.AddTransient(type);
}
return services;
}
}
I then amended the existing controller to take the DI type (note there is just the one ctor)
using ProductsApp.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
namespace ProductsApp.Controllers
{
public class ProductsController : ApiController
{
DefaultProduct _dp = null;
public ProductsController(DefaultProduct dp)
{
_dp = dp;
//
products.Add(dp);
}
List<Product> products = new List<Product>()
{
new Product { Id = 1, Name = "Tomato Soup", Category = "Groceries", Price = 1 },
new Product { Id = 2, Name = "Yo-yo", Category = "Toys", Price = 3.75M },
new Product { Id = 3, Name = "Hammer", Category = "Hardware", Price = 16.99M }
};
public IEnumerable<Product> GetAllProducts()
{
return products;
}
public IHttpActionResult GetProduct(int id)
{
var product = products.FirstOrDefault((p) => p.Id == id);
if (product == null)
{
return NotFound();
}
return Ok(product);
}
}
}
My Default Dependency Resolver
/// <summary>
/// Provides the default dependency resolver for the application - based on IDependencyResolver, which hhas just two methods
/// </summary>
public class DefaultDependencyResolver : IDependencyResolver
{
/// <summary>
/// Provides the service that holds the services
/// </summary>
protected IServiceProvider serviceProvider;
/// <summary>
/// Create the service resolver using the service provided (Direct Injection pattern)
/// </summary>
/// <param name="serviceProvider"></param>
public DefaultDependencyResolver(IServiceProvider serviceProvider)
{
this.serviceProvider = serviceProvider;
}
/// <summary>
/// Get a service by type - assume you get the first one encountered
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public object GetService(Type serviceType)
{
return this.serviceProvider.GetService(serviceType);
}
/// <summary>
/// Get all services of a type
/// </summary>
/// <param name="serviceType"></param>
/// <returns></returns>
public IEnumerable<object> GetServices(Type serviceType)
{
return this.serviceProvider.GetServices(serviceType);
}
}
I recommend you use Autofac, there are anothers fwk like unity, ninject, the benchmarks autofac has excelent perfomance.
http://www.palmmedia.de/blog/2011/8/30/ioc-container-benchmark-performance-comparison
Here is the integration with MVC (and works with all class)
http://docs.autofac.org/en/latest/integration/mvc.html
The simplest way to implements Dependency Injection in ASP.NET MVC 5 is to use the tool developed by Microsoft itself, called Unity.
You can find many resources on the internet about it, and you can start by reading the official documentation available here: Developer's Guide to Dependency Injection Using Unity
Also, is Dependency injection limited to controllers only or can it work with any class?
It works with any class, in any project, as long as you register the Interface related to the Implementation (if you want to take profit of the IoC pattern), all you have to do then is to add the Interface instantiation in your constructor.
In this video a Microsoft MVP demos dependency injection in MVC5 with AutoFac. Very clear explanation on how to set it up:
Dependency Injection MVC5 Demo
Source code is available on GitHub
From here https://scottdorman.blog/2016/03/17/integrating-asp-net-core-dependency-injection-in-mvc-4/
this line saved me.
services.AddControllersAsServices(typeof(Startup).Assembly.GetExportedTypes()
.Where(t => !t.IsAbstract && !t.IsGenericTypeDefinition)
.Where(t => typeof(IController).IsAssignableFrom(t)
|| t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase)));
I recommend using Windsor, by installing the nuget package Castle Windsor MVC Bootstrapper, then you can create a service that implements IWindsorInstaller, something like this:
public class ServiceRegister : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container,
Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
SomeTypeRequiredByConstructor context = new SomeTypeRequiredByConstructor ();
container.Register(
Component
.For<IServiceToRegister>()
.ImplementedBy<ServiceToRegister>().
DependsOn(Dependency.OnValue<SomeTypeRequiredByConstructor>(context))//This is in case your service has parametrize constructoe
.LifestyleTransient());
}
}
Then inside your controller something like this:
public class MyController
{
IServiceToRegister _serviceToRegister;
public MyController (IServiceToRegister serviceToRegister)
{
_serviceToRegister = serviceToRegister;//Then you can use it inside your controller
}
}
And by default the library will handle sending the right service to your controller by calling the install() of ServiceRegister at start up because it implements IWindsorInstaller
Having started from this thread to figure out how to use Microsoft.Extensions.DependencyInjection in my ASP.NET MVC 5 project, and reading and trying and failing, I finally came up with a solution that I wanted to shamelessly offer to the rest of you.
I pieced together a gist from David Fowler, the example code from Scott Dorman, and added in a bit of my own spice to create library that allows you to simulate ASP.NET Core's Startup in ASP.NET MVC "Classic".
For more information, please take a look at the GitHub repository for Arex388.AspNet.Mvc.Startup. If you're interested you can also read through my blog post about it, here (if it doesn't load, refresh until it does, the server's been giving me troubles and I haven't had time to investigate...). Hope it helps someone!
I have already researched a number of errors based on the message "Make sure that the controller has a parameterless public constructor" and those answers did not appear to help. (see bottom)
Building an ASP.NET WEB API 2 application using Ninject.
I want to have an applicationController which will be polled from an Angular service to allow sharing some information across sessions. To accomplish this I have built an applicationService designed to be run as a singleton so that each web api session will share the same service. I am using Ninject 3.2.0.0 and have installed (using NPM based on these instructions)
Ninject
Ninject.Web.Common
Ninject.Web.WebApi
Ninject.Web.WebApi.WebHost
Which, by design, created the NinjectWebCommon where I have added the (I believe) necessary bindings. During an initial request the applicationService is instantiated (as expected), but when the call is made to the GET method of the applicationController I get:
An error occurred when trying to create a controller of type
'ApplicationController'. Make sure that the controller has a
parameterless public constructor.
the stack trace reveals:
at System.Linq.Expressions.Expression.New(Type type) at
System.Web.Http.Internal.TypeActivator.Create[TBase](Type
instanceType) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.GetInstanceOrActivator(HttpRequestMessage
request, Type controllerType, Func`1& activator) at
System.Web.Http.Dispatcher.DefaultHttpControllerActivator.Create(HttpRequestMessage
request, HttpControllerDescriptor controllerDescriptor, Type
controllerType)
The fact that it is calling DefaultHttpControllerActivator makes me think that the Ninject Bindings are not correct, but the the only way the applicationService could be instantiated would be due to ninject.
The bindings in NinjectWebCommon
private static void RegisterServices(IKernel kernel)
public static class NinjectWebCommon
{
private static readonly Bootstrapper bootstrapper = new Bootstrapper();
/// <summary>
/// Starts the application
/// </summary>
public static void Start()
{
DynamicModuleUtility.RegisterModule(typeof(OnePerRequestHttpModule));
DynamicModuleUtility.RegisterModule(typeof(NinjectHttpModule));
bootstrapper.Initialize(CreateKernel);
}
/// <summary>
/// Stops the application.
/// </summary>
public static void Stop()
{
bootstrapper.ShutDown();
}
/// <summary>
/// Creates the kernel that will manage your application.
/// </summary>
/// <returns>The created kernel.</returns>
private static IKernel CreateKernel()
{
var kernel = new StandardKernel();
try
{
kernel.Bind<Func<IKernel>>().ToMethod(ctx => () => new Bootstrapper().Kernel);
kernel.Bind<IHttpModule>().To<HttpApplicationInitializationHttpModule>();
RegisterServices(kernel);
return kernel;
}
catch
{
kernel.Dispose();
throw;
}
}
/// <summary>
/// Load your modules or register your services here!
/// </summary>
/// <param name="kernel">The kernel.</param>
private static void RegisterServices(IKernel kernel)
{
kernel.Bind<ScfProvider>().ToSelf().InSingletonScope();
kernel.Bind<ApplicationService>().ToSelf().InSingletonScope().WithConstructorArgument(kernel.Get<ScfProvider>());
kernel.Bind<ApplicationController>().ToSelf().InRequestScope().WithConstructorArgument(kernel.Get<ApplicationService>());
}
}
the ApplicationService:
public sealed class ApplicationService
{
private readonly ICommand _scfUpdater;
private Timer _timer;
public ApplicationService(ScfProvider scfUpdater)
{
if (scfUpdater == null) throw new ArgumentNullException(nameof(scfUpdater));
_scfUpdater = scfUpdater;
_activeUsers = new Dictionary<string, string>();
int scfInterval = int.Parse(ConfigurationManager.AppSettings["SCFUpdateInterval"]);
_timer = new Timer(SCFTick, this, 0, scfInterval);
}
#if DEBUG
public ICommand SCFUpdater => _scfUpdater;
#endif
private void SCFTick(object state)
{
_scfUpdater.Execute();
}
private readonly Dictionary<string, string> _activeUsers;
public Dictionary<string, string> ActiveUsers => _activeUsers;
public void UpdateUser(string userName, string status)
{
if (_activeUsers.ContainsKey(userName))
{
_activeUsers[userName] = status;
}
else
{
_activeUsers.Add(userName, status);
}
}
}
and finally, the applicationController:
public class ApplicationController : ApiController
{
private readonly ApplicationService _applicationService;
public ApplicationController(ApplicationService applicationService)
{
_applicationService = applicationService;
}
// GET: api/application
public IHttpActionResult Get()
{
_applicationService.UpdateUser(User.Identity.Name, "online");
return Ok(_applicationService.ActiveUsers);
}
...
}
(note: once I have this working more complex functionality will be added...but I need this working first...)
Other research:
Parameterless constructor error with Ninject bindings in .NET Web Api 2.1
(I have the correct references and a public c'tor)
Ninject Web Api "Make sure that the controller has a parameterless public constructor." (& several similar)
(As I understand it, WebActivatorEx and NinjectWebCommon resolve this matter)
I have recently ran into the problem with the parameterless constructor.
Having wasted time googling for solutions with solutions that were ok locally but didn't work when deployed the step I took were these.
Uninstalled nugets for Ninject.*
double checked bin folder and deleted any Ninject*.dlls
Commented out all code in the Ninject.Web.Common.cs files and renamed it as I wanted to
keep service bindings etc.. so I could reinstate in the Ninject.Web.Common.cs that would be created after reinstating the Ninject* nuget packages.
I then followed this guide but using the lates Ninject* nuget versions.
https://nodogmablog.bryanhogan.net/2016/04/web-api-2-and-ninject-how-to-make-them-work-together/
do not forget to add this in the newly created Ninject.Web.Common
GlobalConfiguration.Configuration.DependencyResolver = new NinjectDependencyResolver(kernel);
before return kernel; in the private static CreateKernel method.