2015-10-16 76 views
6

我收到以下错误:值不能为空。参数名称:校长User.GetUserId()在控制器的构造函数内失败

如何在控制器的构造函数中访问Identity(userId)?我只能通过将失败的呼叫包装在一个函数中来实现它(下面突出显示)。

有什么我需要注入?

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 
    } 
} 

回答

6

正如@Henk提到的,控制器构造将在ActionContext中已经设置之前执行,因此您将无法使用ContextRequestUser等物业。您需要在请求的上下文中检索userId。

您可以使用action filters的老式方法,它们仍然是MVC6管道的一部分(它也支持通过IAsyncActionFilter的异步操作过滤器)。

既然你想在你的控制器中设置一个属性,那么你可以实现这个最简单的方法就是覆盖控制器类中的OnActionExecuting方法。这是有效的,因为你从Controller继承,它已经实现了IActionFilter

public override void OnActionExecuting(ActionExecutingContext context) 
{ 
    //Get user id 
    userId = User.GetUserId(); 
} 

编辑

如果检查DefaultControllerFactory你会看到:

  1. 第一控制器创建
  2. 那么ActionContext的设定(通过DefaultControllerPropertyActivator这是财产激活器之一):

    var controller = _controllerActivator.Create(actionContext, controllerType); 
    foreach (var propertyActivator in _propertyActivators) 
    { 
        propertyActivator.Activate(actionContext, controller); 
    } 
    
+0

谢谢!这正是我所寻找的,谢谢你解释:) – Toonsylvania

1

当控制器被实例化时,不能保证请求信息在HttpContext中可用。可能根本没有要求。你有理由在构造函数中需要这些信息吗?

编辑
我明白你的问题。我通常做在这样的情况下,是创建一个属性与只有查询每个控制器一次支持字段:

private int? _userId; 
public int UserId 
{ 
    get 
    { 
     if (!_userId.HasValue) 
     { 
      // query from db. 
      _userId = 42; 
     } 
     return _userId.Value; 
    } 
} 
+0

感谢您的回答。是的,我希望我的所有API控制器都能从这个所谓的“BaseController”继承。将使用该userId来公开将在应用程序中使用的其他值。例如,userId用于查询用户的companyId - 它也是该控制器的成员,这样所有的控制器都可以继承它的价值。原因是在每个控制器中有很多查询需要这些值(userId和companyId)。因此,我想在所有控制器中提供一些变量,而无需在每个控制器中运行查询。 – Toonsylvania

+0

@Toonsylvania我理解你的关心。我添加了一个例子。 –