I am getting the following error: Value cannot be null. Parameter name: principal
How can I access Identity (userId) inside the controller's constructor? I can only get it working by wrapping the failed call in a function (both highlighted below).
Is there anything I need to inject?
public class BaseController : Controller {
protected readonly MylDbContext dbContext;
protected readonly string userId;
public BaseController(MylDbContext dbContext) {
this.dbContext = dbContext;
userId = User.GetUserId(); // fails here
}
public string GetUserId() {
return User.GetUserId(); // works here
}
}
As @Henk mentioned, the controller constructor will be executed before the ActionContext has been set, so you won't have access to properties like Context
, Request
or User
. You need to retrieve the userId within the context of a request.
You could use the old-fashioned approach of the action filters which are still part of the MVC6 pipeline (Which also supports async action filters via IAsyncActionFilter
).
Since you want to set a property in your controller, the most simple way you could implement this is by overriding the OnActionExecuting
method in your controller class. This works because you are inheriting from Controller
, which already implements IActionFilter
.
public override void OnActionExecuting(ActionExecutingContext context)
{
//Get user id
userId = User.GetUserId();
}
Edit
If you check the DefaultControllerFactory
you will see that:
then the ActionContext is set (Via the DefaultControllerPropertyActivator
which is one of the property activators):
var controller = _controllerActivator.Create(actionContext, controllerType);
foreach (var propertyActivator in _propertyActivators)
{
propertyActivator.Activate(actionContext, controller);
}
When the controller is instantiated there is no guarantee that the request information is avaiable in the HttpContext yet. There might not be a request at all. Is there reason why you need that information in the constructor?
Edit
I understand your issue. What I usually do in a scenario like this, is create a property with a backing field which only queries once per controller:
private int? _userId;
public int UserId
{
get
{
if (!_userId.HasValue)
{
// query from db.
_userId = 42;
}
return _userId.Value;
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With