C# 在API调用获得记录后更新数据的最佳方法

C# 在API调用获得记录后更新数据的最佳方法,c#,rest,asp.net-web-api,entity-framework-core,C#,Rest,Asp.net Web Api,Entity Framework Core,我目前正在开发一个API,其中一条记录只允许被提取一次。它基本上是一个队列,一旦客户机提取记录,记录上检索到的字段就会标记为true。Get调用仅在检索到的字段为false的情况下拉取记录 控制器: [HttpGet] public virtual IActionResult GetAll([FromQuery] int? limit) { try { return Ok(_repository.Get(limit)

我目前正在开发一个API,其中一条记录只允许被提取一次。它基本上是一个队列,一旦客户机提取记录,记录上检索到的字段就会标记为true。Get调用仅在检索到的字段为false的情况下拉取记录

控制器:

    [HttpGet]
    public virtual IActionResult GetAll([FromQuery] int? limit)
    {
        try
        {
            return Ok(_repository.Get(limit));
        }
        catch
        {
            return new StatusCodeResult(StatusCodes.Status500InternalServerError);
        }
    }
[HttpPut] 
public virtual async Task<IActionResult> MarkAsRetrieved(IEnumerable<int> reportIds, CancellationToken token)
{
    await _repository.MarkRetrievedAsync(reportIds, token).ConfigureAwait(true);
    return Ok();
}
存储库:

    public IQueryable<Report> Get(int? limit)
    {
        IQueryable<Report> reports;

        if (limit == null)
        {
            reports = _context.Reports.Where(r => r.Retrieved == false);
        }
        else
        {
            reports = _context.Reports.Where(r => r.Retrieved == false).Take((int)limit);
        }

        return reports;
    }
public Task MarkRetrievedAsync([FromBody]IEnumerable<int> reportIds, CancellationToken token)
{
    foreach (Report report in reportIds.Select(x => new Report{ReportId = x, Retrieved = false}))
    {
        _context.Reports.Attach(report);
        report.Retrieved = true;
    }
    return _context.SaveChangesAsync(token);
}
publicIQueryable获取(int?限制)
{
可查询的报告;
if(limit==null)
{
reports=\u context.reports.Where(r=>r.Retrieved==false);
}
其他的
{
reports=\u context.reports.Where(r=>r.Retrieved==false).Take((int)limit);
}
返回报告;
}

修改Get调用提取的记录的最佳方法是什么?如果我在从存储库代码返回结果之前进行了修改,那么当控制器实际将IQueryable转换为真实数据时,该字段已更改,它不会提取任何结果,但是控制器似乎不适合对数据库进行这种修改。

我会将数据库中检索到的
位更改为某种句柄——将Guid或记录id更改为记录获取的另一个表或其他唯一值。然后,我将确定句柄,用该句柄更新即将获取的记录,然后检索与该句柄匹配的记录。在任何时候,如果失败,您都可以将检索到的句柄值设置为空,以用于启动的句柄值。

我会将此功能从检索中分离出来。让调用者/客户机指示报告已通过第二次调用成功检索和读取。这会增加一点开销,但会增加弹性。示例:如果在服务器调用后检索失败(可能在浏览器上的网络或客户端应用程序中),则客户端有另一个机会检索数据

控制器:

    [HttpGet]
    public virtual IActionResult GetAll([FromQuery] int? limit)
    {
        try
        {
            return Ok(_repository.Get(limit));
        }
        catch
        {
            return new StatusCodeResult(StatusCodes.Status500InternalServerError);
        }
    }
[HttpPut] 
public virtual async Task<IActionResult> MarkAsRetrieved(IEnumerable<int> reportIds, CancellationToken token)
{
    await _repository.MarkRetrievedAsync(reportIds, token).ConfigureAwait(true);
    return Ok();
}
[HttpPut]
检索到的公共虚拟异步任务标记(IEnumerable ReportId、CancellationToken标记)
{
await\u repository.markretrievedaync(reportid,token).ConfigureAwait(true);
返回Ok();
}
存储库:

    public IQueryable<Report> Get(int? limit)
    {
        IQueryable<Report> reports;

        if (limit == null)
        {
            reports = _context.Reports.Where(r => r.Retrieved == false);
        }
        else
        {
            reports = _context.Reports.Where(r => r.Retrieved == false).Take((int)limit);
        }

        return reports;
    }
public Task MarkRetrievedAsync([FromBody]IEnumerable<int> reportIds, CancellationToken token)
{
    foreach (Report report in reportIds.Select(x => new Report{ReportId = x, Retrieved = false}))
    {
        _context.Reports.Attach(report);
        report.Retrieved = true;
    }
    return _context.SaveChangesAsync(token);
}
公共任务MarkRetrieveAsync([FromBody]IEnumerable ReportId,CancellationToken令牌)
{
foreach(reportIds.Select(x=>newreport{ReportId=x,Retrieved=false})中的报告)
{
_上下文。报告。附加(报告);
report.Retrieved=true;
}
return\u context.SaveChangesAsync(令牌);
}
笔记
  • 只需发送
    报表
    实例的标识符。然后,您可以附加一个具有相同标识符的空实例,并将
    检索的
    属性更新为
    ,只需在相应的存储
    更新
    语句中发送即可

我喜欢这种策略,但当我尝试这种策略时,我发现一个错误,即报告已附加到_context.Reports.Attach(report)行。@KevenDenen-也是一个选项,其缺点是检索报告只是为了再次更新它们,这基本上是一个双步骤,实际上不需要检索。如果类型报告很轻,那没什么大不了的,如果它有像大字符串这样的大列,那么它的开销很大。@KevenDenen-我能看到这种情况发生的唯一原因是:1)传递了一个双id,你可以用
reportIds.Distinct()过滤掉它。选择(…
.2)您的上下文已过时或与调用检索的上下文实例相同。问题出在上下文上。我在Startup.cs文件中将上下文设置为服务。我刚刚调用了一个HttpGet来检索一个列表,所以上下文中已经充满了报告。如果我重新启动API并调用HttpPut而不首先调用HttpGet,则代码中没有错误。从调用主体获取数据的修饰是[FromBody],即公共异步虚拟任务MarkAsRetrieved([FromBody]int[]ids)