KevenDenen KevenDenen -4 years ago 102
reST (reStructuredText) Question

Best way to update data after an API call gets a record

I am currently working on an API where a record should only be allowed to be pulled once. It's basically a queue where once a client pulls the record, the Retrieved field on the record is marked true. The Get calls only pull records where the Retrieved field is false.

Controller:

[HttpGet]
public virtual IActionResult GetAll([FromQuery] int? limit)
{
try
{
return Ok(_repository.Get(limit));
}
catch
{
return new StatusCodeResult(StatusCodes.Status500InternalServerError);
}
}


Repository:

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;
}


What would be the best way to modify the records that have been pulled by the Get call? If I do the modification before returning results from the repository code, then when the controller actually converts the IQueryable to real data, the field has changed and it won't pull any results, but the Controller seems like the wrong place to be doing this sort of modification to the database.

Code for the modification would be something like:

private void MarkRetrieved(IEnumerable<Report> reports)
{
foreach (Report report in reports)
{
report.Retrieved = true;
}
_context.SaveChanges();
}

Answer Source

I would split this functionality away from the retrieval. Let the caller/client indicate that the report has been successfully retrieved and read with a second call. It is a little more overhead but it adds resilience. Example: if there is a failure in the retrieval after the server call (maybe in the network on browser or client app) then the client has another opportunity to retrieve the data.

Controller:

[HttpPut] 
public virtual async Task<IActionResult> MarkAsRetrieved(IEnumerable<int> reportIds, CancellationToken token)
{
    await _repository.MarkRetrievedAsync(reportIds, token).ConfigureAwait(true);
    return Ok();
}

Repository:

public Task MarkRetrievedAsync(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);
}

Notes

  • Send the data in the body of the request, I forget off hand what the correct decoration attribute is
  • It is only necessary to send over the identifier for a Report instance. You can then attach an empty instance with that same identifier and update the Retrieved property to true, just that will be sent in the corresponding store update statement.

I would double check this to ensure that EF-Core handles this the same way EF6 does

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download