【发布时间】:2016-03-27 02:53:57
【问题描述】:
我已经创建了一个接口
public interface ICurrentUser
{
Task<bool> Set(UserAuth user);
User Get();
}
还有一个班级
public class CurrentUserSvc : Interface.ICurrentUser
{
private User _u;
private UserAuth _ua;
private AppDbContext db;
public CurrentUserSvc(AppDbContext db) {
this.db = db;
}
public User Get()
{
return _u;
}
public async Task<bool> Set(UserAuth ua)
{
_ua = ua; // this is the default EntityFramework IdentityUser
_u = await db.AppUsers // this is my applicaiton's 'extra settings'
// user used to ensure passowrd fields are
// not passed about everywhere
.Where(u => u.UserID == _ua.UserID)
.SingleAsync();
return true;
}
}
在Startup.cs我设置
services.AddScoped<ICurrentUser, CurrentUserSvc>();
// I also add a service which will be used later in a scoped
// lifecycle (though I've also tried transient on that one)
services.AddScoped<IProductDbSvc, ProductDbSvc>();
稍后我调用了一个中间件:
public async Task<Task> Invoke(HttpContext hc)
{
if (hc.User.Identity.IsAuthenticated) {
UserAuth iu = await _um.FindByIdAsync(hc.User.GetUserId());
await _cus.Set(iu);
}
// the values are definitely set correctly here.
// I have inspected them during debug
return _next(hc);
}
稍后我仍然尝试访问CurrentUserSvc的内容我尝试通过GET访问当前用户
public ProductDbSvc(AppDbContext db, ICurrentUser cu){
this.db = db;
this.cu = cu;
// the values in cu are NULL here. Get() returns null
this.CurrentUser = cu.Get();
}
但 Get() 的结果是 null 我原以为 Scoped 参数会保留请求生命周期早期设置的值。
我错过了什么?是否有其他方法可以确保作用域单例在整个应用程序的生命周期中保留用户数据。
更新:我创建了一个通用项目来概括说明这个问题。 https://github.com/AlexChesser/AspnetIdentitySample
- 查看回购
- 在 visualstudio 或 DNX 中构建和运行
- 注册本地用户
- 尝试在http://localhost:5000/api/currentuser上查看服务
您会注意到,在 DEBUG 输出中,您可以看到设置了正确的用户详细信息,但在实际控制器本身中,返回的值为 null。
更新 2 工作样本在 github https://github.com/AlexChesser/AspnetIdentitySample/tree/dependencyinjectionscoped的这个分支上
UPDATE 3 结果表明作用域参数也可以注入到自定义中间件的 INVOKE 方法中。 https://github.com/AlexChesser/AspnetIdentitySample/commit/25b010a5ae45678c137b2ad05c53ccd659a29101 更改调用方法将允许正确注入作用域参数。
public async Task Invoke(HttpContext httpContext,
ICurrentUserService cus,
UserManager<ApplicationUser> um)
{
if (httpContext.User.Identity.IsAuthenticated)
{
ApplicationUser au = await um.FindByIdAsync(httpContext.User.GetUserId());
await cus.Set(au);
}
await _next(httpContext);
}
更新 4 - 我昨晚发现中间件签名存在问题,这非常重要。上面的代码已被编辑为正确的形式。具体方法是Task<Task>和return _next(...)
这导致某些页面加载时出现“白屏”死机(异步调用不当不会引发堆栈跟踪)
通过更改为Task 并使用await next(...),代码可以正常运行,并消除了由于dotnet5 中的异步执行不当而导致的间歇性白屏死机。
【问题讨论】:
-
if (hc.User.Identity.IsAuthenticated) {可能是由于此检查而未设置用户?建议您在应用程序中使用 Logging,以方便调试。 -
抱歉,我应该提到的是经过身份验证的检查确实正确通过并按预期设置了当前用户服务的值
-
@AlexC:据我在 github 存储库中的代码所见,
ICurrentUserService是通过构造函数注入的,但中间件只被实例化一次,即它们是单例的,所以不是在那里注入,尝试通过invoke方法中传入的HttpContext来resolve这个服务...你可以使用RequestServices属性来解决它 -
酷!您是如何发现可以通过
Invoke注入的? -
@davidfowl 在对 github 上一个问题的评论中告诉我。
标签: c# dependency-injection asp.net-identity asp.net-core entity-framework-core