I built a simple player-tracking API app in ASP.NET Core 3.1 that uses Azure Cosmos DB as its back end.
The API to create a new player entry first checks if an entry with the same ID under a given partition key already exists in Cosmos DB using this:
try
{
ItemResponse<Player> response = await this._playerContainer.ReadItemAsync<Player>(playerid, new PartitionKey(partitionkey));
return Conflict();
}
catch (CosmosException ex) when (ex.StatusCode == System.Net.HttpStatusCode.NotFound)
{
// There is some more logic happening here so I cannot directly just call CreateItemAsync()... and return the Conflict response that this might return.
ItemResponse<GameResult> response = await this._gameResultContainer.CreateItemAsync<GameResult>(gameresult, new PartitionKey(gameresult.PlayerId));
// ...
return Accepted();
}
Only if this returns nothing, I go ahead and put the create request in a backend worker queue. Otherwise I return a 409-Conflict to the API caller.
The actual insert happens in an async backend worker. But I want to return to the API caller directly, if his insert will succeed.
All working fine so far. The issue I have is the following: As I am using the Azure Application Insights SDK, any call which does not find an existing item (which should be the normal case here), will automatically create a Error in AppInsights - even though I catch the exception in my code. That's obviously cluttering my logging quite a bit.
Any idea how I can get rid of that or generally how to change the API to get a better behavior for this?
The issue is on the Cosmos DB .NET SDK side. It throws an exception if a document is not found. They can't really change this behavior because clients are relying on it. GitHub Issue
The suggested workaround is to use lower-level Stream API. This way you'll be able to handle 404 behavior on your side.
Something like this:
using (ResponseMessage responseMessage = await container.ReadItemStreamAsync(
partitionKey: new PartitionKey(partitionkey),
id: playerid))
{
if (responseMessage.StatusCode == System.Net.HttpStatusCode.NotFound)
{
...
return Accepted();
}
if (responseMessage.IsSuccessStatusCode)
{
return Conflict();
}
}
There's sample code in the repo for doing custom deserialization