使用 SQL Server 模拟从 Web API 方法返回 IQueryable
Posted
技术标签:
【中文标题】使用 SQL Server 模拟从 Web API 方法返回 IQueryable【英文标题】:Returning IQueryable from Web API methods using SQL Server Impersonation 【发布时间】:2021-04-26 23:59:38 【问题描述】:我有一个 .NET Framework 4.6.1 WebAPI,它公开端点以执行 CRUD 操作。我的客户希望在单个用户帐户的上下文中执行所有操作,因此我使用 SQL Server 模拟来使其工作(所有语句都写入审计文件,因此使用 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 Server 会引发异常。此操作的审计文件将包含语句 EXECUTE AS USER = [USERNAME],[Statement], REVERT
除了返回 IQueryable 的端点外,所有 CRUD 操作都有效。我需要返回一个 IQueryable 以支持 oData 规范,该规范允许客户端传递查询字符串参数来修改查询(例如,包含 $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 语句随事务“执行”,或者有更好的方法来实现我的目标?
【问题讨论】:
您能否澄清一下:“语句需要在事务中执行,否则 SQL Server 会引发异常。”您可能需要手动处理 SqlConnection 生命周期,而不是将其留给 DbContext。还有什么版本的EF? 【参考方案1】:有没有办法让 IQueryable 语句随事务“执行”,或者有更好的方法来实现我的目标?
是的,只需使用 ToList() 将查询转换为实际的结果列表。这当然会将整个Employees表获取到客户端并在应用程序中进行过滤,这通常不是一个好主意。
另一种方法是采用一个参数来描述应该如何完成查询
[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;
现在调用者可以指定他想要的任何类型的过滤,同时仍然确保查询在事务中运行。替代方法是使用 Func<Employee, bool>
在 Where 语句中使用。或者Func<IQueryable<Employee>, Task<T>>
,以防调用者想要更具体。
【讨论】:
以上是关于使用 SQL Server 模拟从 Web API 方法返回 IQueryable的主要内容,如果未能解决你的问题,请参考以下文章
如何使用 web api 将图像和文本从 android 应用程序发送到 sql server
用于使用 Azure SQL Server 数据的 C# Web API
无法从 NET Core 2.2 Web API 连接到 docker sql server