这是我的 ODATA 控制器,具有 ODATA 操作。
public class MyController : ODataController
{
private readonly Repository_repo;
public MyController(IRepository repo)
{
_repo = repo;
}
[EnableQuery(PageSize = 10, EnsureStableOrdering = false)]
public IActionResult Get()
{
var data = _repo.GetData();
return Ok(data)
}
}
这是我的存储库方法。
public IQueryable<DataModel> GetData() => _db.DbSet.Select(data=> new DataModel
{
// Implement model
}).
现在我明白将存储库中的 GetData 方法设置为异步是没有意义的,因为这只是返回一个可查询的对象,在您调用它的枚举器之前它不会被执行。
所以异步的部分是action方法。我如何使其成为异步等待调用?据我所知,Odata EnableQuery 方法需要 IQueryable。
您无需在 OData 中指定或亲手应用 EF Core 异步操作。
根据 Microsoft 的说法,当您返回
IQueryable
时,OData 可以使用 ef 核心功能执行。
见 https://learn.microsoft.com/en-us/odata/webapi/first-odata-api#update-the-controller
但是,答案有点隐含,所以我测试了一下。
我创建了一个
BlockNonAsyncQueriesInterceptor
,它基本上阻止(抛出异常并且不允许执行查询)非异步数据库方法。
public class BlockNonAsyncQueriesInterceptor : DbCommandInterceptor
{
private const string SyncCodeError = @"Synchronous code is not allowed for performance reasons.";
private readonly ILogger _logger;
public BlockNonAsyncQueriesInterceptor(ILogger logger)
{
_logger = logger;
}
public override ValueTask<InterceptionResult<int>> NonQueryExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<int> result, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Ok NonQueryExecutingAsync");
return base.NonQueryExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<int> NonQueryExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<int> result)
{
_logger.LogError("Error NonQueryExecuting");
throw new InvalidOperationException(SyncCodeError);
}
public override ValueTask<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Ok ReaderExecutingAsync");
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
_logger.LogError("Error ReaderExecuting");
throw new InvalidOperationException(SyncCodeError);
}
public override ValueTask<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result, CancellationToken cancellationToken = default)
{
_logger.LogInformation("Ok ScalarExecutingAsync");
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
_logger.LogError("Error ScalarExecuting");
throw new InvalidOperationException(SyncCodeError);
}
}
然后,在我检查了使用 OData 和实体框架的虚假端点后
Queryable
,我注意到 OData 执行实际上都是异步的。
请求后查看输出:
Ok ReaderExecutingAsync
要查看有关这些测试的更多详细信息,请参阅:https://github.com/GuilhermeBley/TestODataWithEf
你不需要。操作方法只是请求管道的一部分;它不是异步的并不排除管道中的其他事物是异步的。处理 OData 查询的中间件实际上是将查询发送到数据库,并且很可能是异步的(我不熟悉源代码,也不能明确地说)。无论如何,如果您实际上在其中执行异步操作,则您的操作只需异步即可。否则不用担心。
您需要返回
IAsyncEnumerable<T>
或 ActionResult<IAsyncEnumerable<T>>
以确保调用是异步的。
使用 8.2.3+ 中的 Microsoft.AspNetCore.OData
进行async 调用的示例
[HttpGet]
[EnableQuery]
public IAsyncEnumerable<Customer> Get()
{
return this.context.Customers.AsAsyncEnumerable();
}
[HttpGet("{id}")]
[EnableQuery]
public ActionResult<IAsyncEnumerable<Customer>> GetById(string id)
{
if (string.IsNullOrWhiteSpace(id))
{
return BadRequest();
}
return Ok(this.context.Customers.AsAsyncEnumerable());
}
查看 GitHub 讨论,了解来自 OData 存储库贡献者的更多详细信息和答案。
如果您的项目已经有一些端点需要调整为异步,您还可以创建自定义
EnableQueryAsyncAttribute
来简化代码更改。
Shiney的部分解决方案可能无法涵盖所有情况:
public class CustomEnableQueryAttribute : EnableQueryAttribute
{
public override void OnActionExecuted(ActionExecutedContext actionExecutedContext)
{
base.OnActionExecuted(actionExecutedContext);
if (actionExecutedContext.Result is ObjectResult { DeclaredType: null } objectResult)
{
if (objectResult.Value is IAsyncEnumerable<Customer>)
{
objectResult.DeclaredType = typeof(IAsyncEnumerable<Customer>);
}
}
}
}