具有异步 Ajax 请求的同步 ActionFilter
Synchronous ActionFilter with Async Ajax Request
当我尝试在页面加载时向具有相同 IActionFilter 属性的 2 个 MVC 操作发出 2 个异步 ajax 请求时遇到并发问题。
SimpleInjector注册如下
public static Container Initialize(IAppBuilder app)
{
var container = GetInitializeContainer(app);
RegisterGlobalFilters(GlobalFilters.Filters, container);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
return container;
}
private static Container GetInitializeContainer(IAppBuilder app)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.RegisterSingleton(app);
RegisterCustomDependacies(container);
container.Register(() => new ApplicationDbContext(), Lifestyle.Scoped);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
//At the moment of verification, there is now HttpContext to check. So create a new owin context to check instead and get the authentication manager.
container.Register(() => AdvancedExtensions.IsVerifying(container) ? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);
return container;
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters, Container container)
{
//Originally had this but it was causing the same issues
//filters.Add(new AuthoriseActionFilter(container.GetInstance<ICacheManager>(),
// container.GetInstance<IUserManager>(),
// container.GetInstance<IAuthorisationManager>(),
// container.GetInstance<IClientManager>(),
// container.GetInstance<ICookieManager>(),
// container.GetInstance<IAuthenticator>()
// ));
container.Register<IActionFilter, AuthoriseActionFilter>(Lifestyle.Scoped);
filters.Add(container.GetInstance<IActionFilter>());
filters.Add(new HandleErrorAttribute());
}
下面是同步 ActionFilter 的 2 个动作
[AuthoriseAction("Can_Access_Summary")]
public async Task<PartialViewResult> ReturnPots(int? dateID, string date) {}
[AuthoriseAction("Can_Access_Summary")]
public async Task<string> GetImportedDates() {}
当ActionFilter 从数据库中请求一些信息时出现问题。我收到以下错误。
There is already an open DataReader associated with this Connection which must be closed first.
这只会在每 4 次页面刷新时发生一次。
下面是我的 IActionFilter 的简化版本
public void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
var attribute = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthoriseAction), false).SingleOrDefault() as AuthoriseAction;
if (attribute != null)
{
var userID = _cookieManager.DecryptFormsAuthenticationCookie(filterContext.HttpContext);
if (userID == -1) { filterContext.Result = new Http401Result(); return; }
var user = _userManager.LoadByIDSync(userID);
var isAuthenticated = _authenticator.AuthenticateUserSync(user);
...
}
}
我可以通过使我的 ajax 请求中的 1 个同步来解决此问题,但这不是真正的解决方案。我也可以通过 1 个请求 return 所有数据,但同样,这不是正确的解决方案。
谢谢
你可以通过简单的扩展方法解决这个问题:
internal static class SimpleInjectorMvcActionFilterExtensions
{
public static void AddFilter<TActionFilter>(
this GlobalFilterCollection filters, Container container)
where TActionFilter : class, IActionFilter
{
// Register instance in the container to allow this instance to be
// diagnosed.
container.Register<TActionFilter>(Lifestyle.Scoped);
// Add a proxy to the global filters that resolves the real instance
filters.Add(new ActionFilterProxy<TActionFilter>(container));
}
private sealed class ActionFilterProxy<TActionFilter> : IActionFilter
where TActionFilter : class, IActionFilter
{
private readonly Container container;
public ActionFilterProxy(Container container) { this.container = container; }
public void OnActionExecuting(ActionExecutingContext c)=> Get().OnActionExecuting(c);
public void OnActionExecuted(ActionExecutedContext c)=> Get().OnActionExecuted(c);
private TActionFilter Get() => container.GetInstance<TActionFilter>();
}
}
您可以按如下方式使用它:
public static void RegisterGlobalFilters(
GlobalFilterCollection filters, Container container)
{
// Call extension method
filters.AddFilter<AuthoriseActionFilter>(container);
filters.Add(new HandleErrorAttribute());
}
不是将 AuthoriseActionFilter
添加到全局过滤器列表(单例列表),而是添加了 ActionFilterProxy<AuthoriseActionFilter>
的实例。这个代理实例可以安全地用作单例,因为它会在每个请求上回调 container
并为每个请求解析一个新的 AuthoriseActionFilter
。
当我尝试在页面加载时向具有相同 IActionFilter 属性的 2 个 MVC 操作发出 2 个异步 ajax 请求时遇到并发问题。
SimpleInjector注册如下
public static Container Initialize(IAppBuilder app)
{
var container = GetInitializeContainer(app);
RegisterGlobalFilters(GlobalFilters.Filters, container);
container.Verify();
DependencyResolver.SetResolver(new SimpleInjectorDependencyResolver(container));
return container;
}
private static Container GetInitializeContainer(IAppBuilder app)
{
var container = new Container();
container.Options.DefaultScopedLifestyle = new WebRequestLifestyle();
container.RegisterSingleton(app);
RegisterCustomDependacies(container);
container.Register(() => new ApplicationDbContext(), Lifestyle.Scoped);
container.RegisterMvcControllers(Assembly.GetExecutingAssembly());
//At the moment of verification, there is now HttpContext to check. So create a new owin context to check instead and get the authentication manager.
container.Register(() => AdvancedExtensions.IsVerifying(container) ? new OwinContext(new Dictionary<string, object>()).Authentication
: HttpContext.Current.GetOwinContext().Authentication, Lifestyle.Scoped);
return container;
}
public static void RegisterGlobalFilters(GlobalFilterCollection filters, Container container)
{
//Originally had this but it was causing the same issues
//filters.Add(new AuthoriseActionFilter(container.GetInstance<ICacheManager>(),
// container.GetInstance<IUserManager>(),
// container.GetInstance<IAuthorisationManager>(),
// container.GetInstance<IClientManager>(),
// container.GetInstance<ICookieManager>(),
// container.GetInstance<IAuthenticator>()
// ));
container.Register<IActionFilter, AuthoriseActionFilter>(Lifestyle.Scoped);
filters.Add(container.GetInstance<IActionFilter>());
filters.Add(new HandleErrorAttribute());
}
下面是同步 ActionFilter 的 2 个动作
[AuthoriseAction("Can_Access_Summary")]
public async Task<PartialViewResult> ReturnPots(int? dateID, string date) {}
[AuthoriseAction("Can_Access_Summary")]
public async Task<string> GetImportedDates() {}
当ActionFilter 从数据库中请求一些信息时出现问题。我收到以下错误。
There is already an open DataReader associated with this Connection which must be closed first.
这只会在每 4 次页面刷新时发生一次。
下面是我的 IActionFilter 的简化版本
public void OnActionExecuting(ActionExecutingContext filterContext)
{
try
{
var attribute = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AuthoriseAction), false).SingleOrDefault() as AuthoriseAction;
if (attribute != null)
{
var userID = _cookieManager.DecryptFormsAuthenticationCookie(filterContext.HttpContext);
if (userID == -1) { filterContext.Result = new Http401Result(); return; }
var user = _userManager.LoadByIDSync(userID);
var isAuthenticated = _authenticator.AuthenticateUserSync(user);
...
}
}
我可以通过使我的 ajax 请求中的 1 个同步来解决此问题,但这不是真正的解决方案。我也可以通过 1 个请求 return 所有数据,但同样,这不是正确的解决方案。
谢谢
你可以通过简单的扩展方法解决这个问题:
internal static class SimpleInjectorMvcActionFilterExtensions
{
public static void AddFilter<TActionFilter>(
this GlobalFilterCollection filters, Container container)
where TActionFilter : class, IActionFilter
{
// Register instance in the container to allow this instance to be
// diagnosed.
container.Register<TActionFilter>(Lifestyle.Scoped);
// Add a proxy to the global filters that resolves the real instance
filters.Add(new ActionFilterProxy<TActionFilter>(container));
}
private sealed class ActionFilterProxy<TActionFilter> : IActionFilter
where TActionFilter : class, IActionFilter
{
private readonly Container container;
public ActionFilterProxy(Container container) { this.container = container; }
public void OnActionExecuting(ActionExecutingContext c)=> Get().OnActionExecuting(c);
public void OnActionExecuted(ActionExecutedContext c)=> Get().OnActionExecuted(c);
private TActionFilter Get() => container.GetInstance<TActionFilter>();
}
}
您可以按如下方式使用它:
public static void RegisterGlobalFilters(
GlobalFilterCollection filters, Container container)
{
// Call extension method
filters.AddFilter<AuthoriseActionFilter>(container);
filters.Add(new HandleErrorAttribute());
}
不是将 AuthoriseActionFilter
添加到全局过滤器列表(单例列表),而是添加了 ActionFilterProxy<AuthoriseActionFilter>
的实例。这个代理实例可以安全地用作单例,因为它会在每个请求上回调 container
并为每个请求解析一个新的 AuthoriseActionFilter
。