如何使用可等待查询以异步方式调用 OData 操作

问题描述 投票:0回答:3

这是我的 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。

c# asp.net-core odata microsoft-odata
3个回答
1
投票

您无需在 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


0
投票

你不需要。操作方法只是请求管道的一部分;它不是异步的并不排除管道中的其他事物是异步的。处理 OData 查询的中间件实际上是将查询发送到数据库,并且很可能是异步的(我不熟悉源代码,也不能明确地说)。无论如何,如果您实际上在其中执行异步操作,则您的操作只需异步即可。否则不用担心。


0
投票

您需要返回

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>); } } } }
    
© www.soinside.com 2019 - 2024. All rights reserved.