具有异步Ajax请求的同步ActionFilter

时间:2017-08-04 11:19:16

标签: c# asp.net ajax asynchronous simple-injector

尝试在页面加载时将2个异步ajax请求转换为具有相同IActionFilter属性的2个MVC操作时出现并发问题。

SimpleInjector注册如下

Product.find(req.params.isbn, function(err, product){
    if(!product) res.status(404).send({message: 'Product not exits'});
    console.log("PUT - /products/" + req.params.isbn);
    product.isbn = req.body.isbn;
    product.title = req.body.title;
    product.author = req.body.author;
    product.active = req.body.active;
    product.template = req.body.template;

   let updateproduct = new Product(product);

    updateproduct.save(function(err){
        if(err) return res.send(500,err.message);
        console.log("Successfully updated: " + req.body.isbn);
        res.status(200).json(product);
    });
});
};

以下是同步ActionFilter的2个动作

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从数据库请求某些信息时,会出现此问题。我收到以下错误。

  

已经有一个与此Connection关联的打开DataReader,必须先关闭它。

每4页刷新一次只会发生一次。

以下是我的IActionFilter的简化版本

[AuthoriseAction("Can_Access_Summary")]
public async Task<PartialViewResult> ReturnPots(int? dateID, string date) {}


[AuthoriseAction("Can_Access_Summary")]
public async Task<string> GetImportedDates() {}

我可以通过使我的一个ajax请求同步来解决这个问题,但这不是一个真正的解决方案。我也可以通过1个请求返回所有数据,但同样,它不是真正正确的解决方案。

由于

1 个答案:

答案 0 :(得分:1)

You can solve this problem with a simple extension method:

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>();
    }
}

You can use this as follows:

public static void RegisterGlobalFilters(
    GlobalFilterCollection filters, Container container)
{
    // Call extension method
    filters.AddFilter<AuthoriseActionFilter>(container);
    filters.Add(new HandleErrorAttribute());
}

Instead of adding the AuthoriseActionFilter to the list of global filters (a list of singletons), an instance of ActionFilterProxy<AuthoriseActionFilter> is added. This proxy instance can safely be used as singleton, because it will call back into the container on each request and resolve a new AuthoriseActionFilter for each request.