【问题标题】:Is there a way to create an ActionFilter that wraps the content of the Action in a using statement?有没有办法创建一个 ActionFilter 将 Action 的内容包装在 using 语句中?
【发布时间】:2014-06-11 12:37:36
【问题描述】:

我的场景:我的应用程序是一个 Web Api 2 应用程序,它使用业务逻辑和存储库层进行数据访问。 Web 应用程序使用 ASP.NET Impersonation 作为访问网站的用户登录到数据库(通过 PKI 进行身份验证)。我有几个异步控制器方法。但是,当我在数据访问方法上await 时,数据库调用可能会在不同的线程上完成,然后该线程将以我的应用程序池的身份访问数据库,该应用程序池不允许连接到数据库。

示例控制器

public class TestApiController : ApiController {
    private IBusinessLogicObject _myBlObject;

    public TestApiController(IBusinessLogicObject myBlObject){
        _myBlObject = myBlObject; //Populated through Unity
    }

    public async Task<int> CountMyJobs(string name){
        return await _myBlObject.CountMyJobsAsync(name);
    }
}

示例业务逻辑类

public class BusinessLogicObject : IBusinessLogicObject
{
    private IGenericRepository<Job> _jobRepository;

    public BusinessLogicObject(IGenericRepository<Job> _jobRepository)
    {
        _jobRepository = jobRepository; //Populated with Unity
    }

    public Task<int> CountMyJobsAsync(string name)
    {
        using (WindowsIdentity.GetCurrent().Impersonate())
        {
            //JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
            return _jobRepository.Where(i => i.Name == name).CountAsync();
        }        
    }
}

如果我将 using 语句移动到控制器中(包裹在 await 周围),它可以正常工作。

问题似乎是因为await 在模拟上下文之外,它不会模拟数据库调用(CountAsync())并且我无法打开与我的数据库的连接。

问题

有没有办法可以在我的控制器方法上编写ActionFilter 或其他属性,以便方法本身(包含等待调用)将自动包装在 using 语句中?

【问题讨论】:

    标签: c# async-await asp.net-web-api impersonation action-filter


    【解决方案1】:

    merpmerp 的答案在多线程服务器中会出现问题。由于每个修饰方法只有一个 ActionFilterAttribute 实例,因此对同一方法的两个同时请求将导致 usingVariable 被覆盖,并且最终只会释放一个。

    您需要更进一步,将 ImpersonationContext 存储在请求上下文中的某个位置——例如在 filterContext.Request.Properties 中。

    【讨论】:

      【解决方案2】:

      我不相信有一种方法可以在 using 语句中使用属性实际包装方法,但您基本上可以通过在自定义 ActionFilter 中使用 OnActionExecuting 和 OnResultExecuted 方法来做同样的事情。

      public class IdentityImpersonateActionFilter : ActionFilterAttribute
      {
          IDisposable usingVaribale;
      
          public override void OnActionExecuting(ActionExecutingContext filterContext)
          {
              usingVaribale = WindowsIdentity.GetCurrent().Impersonate();
          }
      
          public override void OnResultExecuted(ResultExecutedContext filterContext)
          {
              usingVaribale.Dispose();
          }
      }
      

      然后你可以用[IdentityImpersonate]装饰你的方法或整个控制器类

      [IdentityImpersonate]    
      public Task<int> CountMyJobsAsync(string name)
      {
          //JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
          return _jobRepository.Where(i => i.Name == name).CountAsync();  
      }
      

      如果你愿意,你也可以在你的函数中使用变量来访问它

      public override void OnActionExecuting(ActionExecutingContext filterContext)
      {
          usingVaribale = WindowsIdentity.GetCurrent().Impersonate();
          filterContext.ActionParameters.Add("parameterName", usingVaribale);
      }
      

      并将参数添加到您的控制器函数中

      [IdentityImpersonate]    
      public Task<int> CountMyJobsAsync(object parameterName, string name)
      {
          //JobRepository is effectively a DbSet<Job> and this call returns IQueryable<Job>
          return _jobRepository.Where(i => i.Name == name).CountAsync();  
      }
      

      希望这会有所帮助!

      【讨论】:

        【解决方案3】:

        如果你想让模拟保持业务逻辑的责任,那么你可以这样做:

        public async Task<int> CountMyJobsAsync(string name)
        {
            using (WindowsIdentity.GetCurrent().Impersonate())
            {
                return await _jobRepository.Where(i => i.Name == name).CountAsync()
                    .ConfigureAwait(false);
            }
        }
        

        【讨论】:

        • .ConfigureAwait(false) 调用有什么作用?
        • 它只是避免了重新进入 ASP.NET 请求上下文的开销,直到它实际上需要更进一步的堆栈。
        • 不幸的是,这并不能解决问题。它仍然以应用程序池身份而不是模拟身份执行数据库调用。
        • 如果你使用Count而不是CountAsync是否有效?
        猜你喜欢
        • 2023-04-08
        • 2021-11-07
        • 1970-01-01
        • 2010-09-08
        • 1970-01-01
        • 2021-10-26
        • 1970-01-01
        • 1970-01-01
        • 2020-09-22
        相关资源
        最近更新 更多