有没有一种方法可以创建一个ActionFilter,将Action的内容封装在using语句中

本文关键字:Action 封装 using 语句 一个 方法 一种 创建 ActionFilter 有没有 | 更新日期: 2023-09-27 18:00:05

我的场景:我应用程序是一个Web Api 2应用程序,使用业务逻辑和存储库层进行数据访问。web应用程序使用ASP.NET模拟作为访问网站的用户登录到数据库(通过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在模拟上下文之外,所以它不会模拟数据库调用(CountAsync()),并且我无法打开与数据库的连接。

问题

有没有办法在我的控制器方法上写一个ActionFilter或其他一些属性,这样方法本身(包含等待调用)就会自动包装在using语句中?

有没有一种方法可以创建一个ActionFilter,将Action的内容封装在using语句中

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

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

我不认为有一种方法可以用属性将方法包装在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();  
}

希望这能有所帮助!

如果您想让模拟由业务逻辑负责,那么您可以这样做:

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