有什么方法可以通过 "http request" 和通过 DI 在 blazor 组件中让每个用户在 "DI scope" 中保持持久性?

is there any way to have persistence per user in a "DI scope" both through "http request" and in blazor components via DI?

我通常使用“Windows 身份验证”并且总是有一个带有记录用户信息的 HTTPContext。

我观察到“PerScope”DI 并不总是保留,尤其是在向 Blazor 组件(Blazor 服务器)请求“Per Scope”服务时。

我通过使用按用户名缓存的范围字典创建单例解决了这个问题。

    public class PerUserPersistentScopeService
            {
                private readonly MemoryCache _usersCache;
                private readonly IServiceScopeFactory _serviceScopeFactory;
                
                public PerUserPersistentScopeService(IServiceScopeFactory scopeFactory)
                {
                    _serviceScopeFactory = scopeFactory;
                    _usersCache = new MemoryCache(new MemoryCacheOptions(){ExpirationScanFrequency = new TimeSpan(0,5,0)});
                }
...
               public T GetService<T>(){
                  using here httpcontext.user.identity.name to obtain
                         a per use service
               }

有了这个单例,我总是得到每个用户范围的 GetService。

但是

现在,当我使用 Blazor 组件(服务器端)时,IIS 服务器(非 IIS Express)不会 return 在 windows 身份验证下的 HTTPContext 中的用户身份。

有些组件非常通用(例如:导航面板等...),而其他组件则非常具体,以至于级联这么多复杂的每个用户实体是不切实际的。

有没有办法让通用的 Scoped DI 在 blazor 页面和组件上恢复工作?

或者它也可以...

是否有任何其他方法来获取(没有级联参数)向其提供 blazor 组件或 blazor 页面的用户的名称?

  1. 创建可序列化的 ContextIdentity Class (class 必须有一个空的构造函数)
[Serializable] public class ContextIdentity { public string Name { get; set; } public string AuthenticationType { get; set; } public bool IsAuthenticated { get; set; } public string Domain { get; set; } public string Account { get; set; } public ContextIdentity() { } public ContextIdentity(string name, string authenticationType, bool isAuthenticated) { Name = name; AuthenticationType = authenticationType; IsAuthenticated = isAuthenticated; if (isAuthenticated) { var sp = Name.Split('\\'); if (sp.Length == 2) { Domain = sp[0]; Account = sp[1]; } else { Domain = ""; Account = Name; } } } public ContextIdentity(IIdentity identity): this(identity?.Name,identity?.AuthenticationType, identity?.IsAuthenticated??false) { } }
  1. 创建一个名为 ContextIdentityProvider 的作用域服务
public class ContextIdentityProvider { public ContextIdentity Identity { get; set; } }
  1. 编辑App.razor以注入身份
<pre><code>@inject ContextIdentityProvider contextIdentityProvider &lt;CascadingAuthenticationState&gt; &lt;Router AppAssembly="@typeof(Program).Assembly" PreferExactMatches="@true"&gt; &lt;Found Context="routeData"&gt; &lt;AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /&gt; &lt;/Found&gt; &lt;NotFound&gt; &lt;LayoutView Layout="@typeof(MainLayout)"&gt; &lt;p&gt;Sorry, there's nothing at this address.&lt;/p&gt; &lt;/LayoutView&gt; &lt;/NotFound&gt; &lt;/Router&gt; &lt;/CascadingAuthenticationState&gt; @code{ &#91;Parameter&#93; public ContextIdentity ContextIdentity { get; set; } protected override void OnInitialized() { base.OnInitialized(); contextIdentityProvider.Identity = ContextIdentity; } }
  1. 要将 ContextIdentity 跨不同的选项卡保存到同一用途,请为同一用户创建 SCOPE 的 SINGLETON 存储库服务。
public class PerUserPersistentScopeService { private readonly MemoryCache _usersCache; private readonly IServiceScopeFactory _serviceScopeFactory; public PerUserPersistentScopeService(IServiceScopeFactory scopeFactory) { _serviceScopeFactory = scopeFactory; _usersCache = new MemoryCache( new MemoryCacheOptions(){ ExpirationScanFrequency = new TimeSpan(0,5,0) }); } public IServiceProvider GetProvider(ContextIdentity identity) { if (identity is null) return null; if (!identity.IsAuthenticated) return null; var serviceScope = _usersCache.GetOrCreate(identity.Name, entry => { entry.SlidingExpiration = new TimeSpan(0, 15, 0); var e=_serviceScopeFactory.CreateScope(); var newidentity = e.ServiceProvider.GetService(); newidentity.Identity = identity; return e; }); return serviceScope.ServiceProvider; } public T GetService(ContextIdentity identity) where T:class { var p = GetProvider(identity); if (p is null) return null; return p.GetService(); } }

注意:创建新范围服务提供者时,必须将上下文标识重新注入到新范围。