[英]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.