Hi Team,
I am testing a simple ASP.NET Core async application with AppDynamics .NET Agent.
The agent is successfully detecting:
- Business Transactions
- HTTP Exit Calls
- Database Calls
- Service Topology
- Async timings
I can see the transaction flow map correctly between ServiceA and ServiceB, and database calls are also visible.
However, in the Transaction Snapshot -> Call Graph / Function Trace view, the “Exit Calls / Threads” column is empty for async execution.
Application Flow:
1. ASP.NET Core endpoint receives request
2. Async HTTP call using HttpClient
3. Async SQLite DB operations
4. Response returned successfully
Sample Code:
```csharp
using Microsoft.AspNetCore.Builder;
using Microsoft.Data.Sqlite;
using Microsoft.Extensions.Hosting;
using System.Net.Http;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
string dbPath = "Data Source=test.db";
using (var conn = new SqliteConnection(dbPath))
{
conn.Open();
var cmd = conn.CreateCommand();
cmd.CommandText =
"CREATE TABLE IF NOT EXISTS Test (Id INTEGER PRIMARY KEY, Name TEXT)";
cmd.ExecuteNonQuery();
}
app.MapGet("/call", async () =>
{
var httpClient = new HttpClient();
var response =
await httpClient.GetStringAsync("http://localhost:5002/api/hello");
using var conn = new SqliteConnection(dbPath);
await conn.OpenAsync();
var insertCmd = conn.CreateCommand();
insertCmd.CommandText =
"INSERT INTO Test (Name) VALUES ('APM Test')";
await insertCmd.ExecuteNonQueryAsync();
var selectCmd = conn.CreateCommand();
selectCmd.CommandText = "SELECT COUNT(*) FROM Test";
var count =
(long)await selectCmd.ExecuteScalarAsync();
return new
{
message = "Service A response",
serviceB = response,
dbCount = count
};
});
app.Run();
Questions:
I have attached screenshots of:
Any clarification would be helpful.
Thanks.
Thanks for the clearer shot. I had one of the numbers wrong: the HTTP call is 232 ms, not 4 ms. That's worth correcting because it actually splits the answer into two different reasons.
Reading the flow map properly: the HTTP call from ServiceA to ServiceB is 232 ms (async), the ADO.NET/SQLite call to test.db is 4 ms (async), and ServiceA's own work is about 5 ms. So you've got one exit well above the call graph threshold and one well below it, and they're missing from the Exit Calls/Threads column for different reasons.
The SQLite call (4 ms) is the simple one. It's below the call graph's minimum capture threshold (min-duration-for-jdbc-call-in-ms, 10 ms by default for SQL/ADO.NET), so it never gets drawn in that column even though the agent recorded it. Drop that threshold toward 1 ms on the test node and it'll start showing.
The HTTP call (232 ms) is the interesting one, and the threshold doesn't explain it, because 232 ms is far over any limit. This is the async continuation behavior. When you await the HttpClient call, the method yields and the real 232 ms wait happens on a detached continuation, not inline on the synchronous frames the sampler is showing you (the AsyncTaskMethodBuilder and TaskAwaiter rows). For async exits the agent doesn't nest the call under your method, it surfaces it as an "await@TierName" link in the Exit Calls/Threads column, and it lists every exit in the snapshot's DB & Remote Service Calls tab regardless. So the reliable place to confirm the 232 ms call is that DB & Remote Service Calls tab and the flow map. One thing to check first: make sure the snapshot you're drilling into is actually a full /call instance that contains the HTTP hop (something around the 252 ms total), not a short instance that never made the call. If the Exit Calls/Threads column is still empty on a snapshot that clearly includes the 232 ms call, that points at how your agent renders async rather than at a missing exit.
So to your four questions, now with the right numbers:
One caveat worth knowing: the await link in the Exit Calls/Threads column is documented for the .NET Framework agent. The .NET Core agent can represent async exits differently, so if you never find an await link, treat the DB & Remote Service Calls tab and the flow map as the source of truth for that 232 ms call.
Worth a read:
On the drill-down, if you open a snapshot that actually shows the full /call (around the 252 ms total, not the 8 ms or 23 ms instances), do you see the 232 ms HTTP call or an await@ServiceB link in the DB & Remote Service Calls tab or the Exit Calls/Threads column? That'll tell us whether your agent renders async exits with the await-link style at all, or only lists them in the exit-call tab.