【问题标题】:AddScoped service is not retaining settings throughout the requestAddScoped 服务未在整个请求中保留设置
【发布时间】: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

您会注意到,在 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&lt;Task&gt;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


【解决方案1】:

DbContext 是范围服务,您的 CurrentUserSvc 是范围服务。中间件在应用程序的整个运行时间内只实例化一次,因此它们本质上是单例的。因此,您需要将DbContextCurrentUserSvc 都从这里注入的构造函数中删除。

相反,您可以使用HttpContextRequestServices 属性(返回IServiceProvider)来解析DbContextCurrentUserSvc 服务。

【讨论】:

  • 这很有趣。所以即使你在每个请求上都会调用它,它也只会实例化一次。这是有道理的。
  • 谢谢基兰!您对 httpcontext 上的范围问题和 RequestServices 是正确的。 github.com/AlexChesser/AspnetIdentitySample/commit/… 此提交包含使 currentuserservice 按我的预期工作所需的更改
【解决方案2】:

在中间件中,将依赖项注入到IServiceProvider,而不是ICurrentUser。然后在Invoke中通过serviceProvider.GetRequiredService&lt;ICurrentUser&gt;();获取当前用户

【讨论】:

  • 不幸的是,这不起作用。这些值在生命周期的后期仍然为空。
  • 感谢斯塔福德的回答!您对 IServiceProvider 的要求是正确的,但是根据 Kiran 在下面的回答,它失败了,我认为与所有其他注入的原因相同。中间件只实例化一次。工作解决方案是将 IServiceProvider 从 HttpContext github.com/AlexChesser/AspnetIdentitySample/commit/… 在此提交中可在 github 中查看的工作代码中拉出。
猜你喜欢
  • 1970-01-01
  • 2012-11-13
  • 1970-01-01
  • 2020-05-26
  • 2019-01-07
  • 1970-01-01
  • 2016-01-19
  • 2013-03-16
  • 2018-01-01
相关资源
最近更新 更多