繁体   English   中英

使用 SQL 服务器模拟从 Web API 方法返回 IQueryable

[英]Returning IQueryable from Web API methods using SQL Server Impersonation

我有一个 .NET Framework 4.6.1 WebAPI,它公开端点以执行 CRUD 操作。 我的客户希望在单个用户帐户的上下文中执行所有操作,因此我使用 SQL 服务器模拟使其工作(所有语句都写入审计文件,因此使用 SQL 模拟允许我们查看谁在执行该语句)。 我从 NameIdentifier 声明中获取用户名,然后使用“EXECUTE AS USER”命令执行语句。

通过 ID 检索单个记录的示例如下:

using (var transaction = SyncDbContext.Database.BeginTransaction())
{         
   await SyncDbContext.Database.ExecuteSqlCommandAsync($"EXECUTE AS USER = '{UserName}'");
   var data = await LookupAsync(id);
   await SyncDbContext.Database.ExecuteSqlCommandAsync("REVERT");
   transaction.Commit();
   return data;
}

这些语句需要在事务中执行,否则 SQL 服务器会抛出异常。 此操作的审计文件将包含语句 EXECUTE AS USER = [USERNAME],[Statement], REVERT

除了返回 IQueryable 的端点外,所有 CRUD 操作都有效。 我需要返回一个 IQueryable 以支持 oData 规范,该规范允许客户端传递查询字符串 arguments 来修改查询(例如,包含 $top=10 的查询字符串参数将在 EF 查询中变为.Take(10))。

以下是返回 IQueryable 的端点示例:

    [EnableQuery(MaxTop = 1000)]
    public async Task<IQueryable<Employee>> GetEmployees()
    {
        using (var transaction = SyncDbContext.Database.BeginTransaction())
        {
            await SyncDbContext.Database.ExecuteSqlCommandAsync($"EXECUTE AS USER = '{UserName}'");
            var data = SyncDbContext.Employees.AsQueryable();
            await SyncDbContext.Database.ExecuteSqlCommandAsync("REVERT");
            transaction.Commit();
            return data;
        }
    }

问题在于,由于 iQueryable 的延迟执行,审计文件包含乱序的语句 - EXECUTE AS USER = [USERNAME]、[REVERT]、[STATEMENT]。 有没有办法让 IQueryable 语句在事务中“执行”,或者有更好的方法来实现我的目标?

有没有办法让 IQueryable 语句在事务中“执行”,或者有更好的方法来实现我的目标?

是的,只需使用 ToList() 将查询转换为实际的结果列表。 这当然会将整个雇员表获取到客户端并在应用程序中进行过滤,这通常不是一个好主意。

另一种方法是采用一个参数来描述应该如何完成查询

[EnableQuery(MaxTop = 1000)]
public async Task<List<Employee>> GetEmployees(Func<IQueryable<Employee>, Task<List<Employee>>> filter)
{
    using (var transaction = SyncDbContext.Database.BeginTransaction())
    {
        await SyncDbContext.Database.ExecuteSqlCommandAsync($"EXECUTE AS USER = '{UserName}'");
        var data = await filter(SyncDbContext.Employees.AsQueryable());
        await SyncDbContext.Database.ExecuteSqlCommandAsync("REVERT");
        transaction.Commit();
        return data;
    }
}

现在调用者可以指定他想要的任何类型的过滤,同时仍然确保查询在事务中运行。 替代方法是在 Where 语句中使用Func<Employee, bool> 或者Func<IQueryable<Employee>, Task<T>>以防调用者想要更具体。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM