有什么方法可以通过 "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 页面的用户的名称?
- 创建可序列化的 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)
{
}
}
- 创建一个名为 ContextIdentityProvider 的作用域服务
public class ContextIdentityProvider
{
public ContextIdentity Identity { get; set; }
}
- 编辑App.razor以注入身份
<pre><code>@inject ContextIdentityProvider contextIdentityProvider
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly"
PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this
address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
@code{
[Parameter]
public ContextIdentity ContextIdentity { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
contextIdentityProvider.Identity = ContextIdentity;
}
}
- 要将 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();
}
}
注意:创建新范围服务提供者时,必须将上下文标识重新注入到新范围。
我通常使用“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 页面的用户的名称?
- 创建可序列化的 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)
{
}
}
- 创建一个名为 ContextIdentityProvider 的作用域服务
public class ContextIdentityProvider
{
public ContextIdentity Identity { get; set; }
}
- 编辑App.razor以注入身份
<pre><code>@inject ContextIdentityProvider contextIdentityProvider
<CascadingAuthenticationState>
<Router AppAssembly="@typeof(Program).Assembly"
PreferExactMatches="@true">
<Found Context="routeData">
<AuthorizeRouteView RouteData="@routeData"
DefaultLayout="@typeof(MainLayout)" />
</Found>
<NotFound>
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this
address.</p>
</LayoutView>
</NotFound>
</Router>
</CascadingAuthenticationState>
@code{
[Parameter]
public ContextIdentity ContextIdentity { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
contextIdentityProvider.Identity = ContextIdentity;
}
}
- 要将 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();
}
}
注意:创建新范围服务提供者时,必须将上下文标识重新注入到新范围。