Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add HttpContextCurrentExecutionSegmentsContainer (#992)
* Add HttpContextCurrentExecutionSegmentsContainer This commit adds an implementation of ICurrentExecutionSegmentsContainer that gets/sets the current transaction and span in HttpContext.Items, in addition to AsyncLocal<T>, making them available within the ExecutionContext of IIS. AsyncLocal<T> are not propagated across the ASP.NET ExecutionContext running in IIS, resulting in the current transaction set in Application_BeginRequest returning as null from Agent.Tracer.CurrentTransaction in later subsequent IIS events, when such events execute asynchronously. ASP.NET does however propagate the current HttpContext through HttpContext.Current, allowing values to be stored in HttpContext.Items hashtable and shared across IIS events. The HttpContextCurrentExecutionSegmentsContainer uses both AsyncLocal<T> and HttpContext.Items to get/set the current transaction and span, allowing them to be available in both IIS events and async contexts. AsyncLocal<T> is still needed for places where HttpContext.Current is null, for example, DiagnosticSourceListeners running on a separate thread. It seems it may be possible for the current transaction and current span instances stored in AsyncLocal<T> and HttpContext.Items to be different in such scenarios where one is available but not the other on beginning the transaction/span, and where availability is reversed on end. Unsure how likely this is in practice however, and using both AsyncLocal<T> and HttpContext.Items is the common approach in resolving this issue in < .NET 4.7.1. For .NET 4.7.1, a new OnExecuteRequestStep method has been added to HttpApplication, to allow the execution context to be restored/saved: https://devblogs.microsoft.com/dotnet/net-framework-4-7-1-asp-net-and-configuration-features/#asp-net-execution-step-feature Closes #934 Closes #972 * tidy after rebase * Add concurrent requests test This commit adds an integration test for HttpContextCurrentExecutionSegmentsContainer that makes multiple concurrent requests and asserts that the captured transactions and span ids align with expectations.
- Loading branch information
Showing
17 changed files
with
661 additions
and
51 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
123 changes: 123 additions & 0 deletions
123
sample/AspNetFullFrameworkSampleApp/Controllers/DatabaseController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System.Collections.Generic; | ||
using System.Data.Entity; | ||
using System.Linq; | ||
using System.Net.Http; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using System.Web.Mvc; | ||
using AspNetFullFrameworkSampleApp.Bootstrap; | ||
using AspNetFullFrameworkSampleApp.Data; | ||
using AspNetFullFrameworkSampleApp.Models; | ||
using Newtonsoft.Json; | ||
|
||
namespace AspNetFullFrameworkSampleApp.Controllers | ||
{ | ||
/// <summary> | ||
/// Demonstrates database functionality | ||
/// </summary> | ||
public class DatabaseController : ControllerBase | ||
{ | ||
/// <summary> | ||
/// List the sample data in the database | ||
/// </summary> | ||
/// <returns></returns> | ||
public ActionResult Index() | ||
{ | ||
using var context = new SampleDataDbContext(); | ||
var samples = context.Set<SampleData>().ToList(); | ||
return View(samples); | ||
} | ||
|
||
/// <summary> | ||
/// Allows sample data to be inserted into the database | ||
/// </summary> | ||
public ActionResult Create() => View(new CreateSampleDataViewModel()); | ||
|
||
[HttpPost] | ||
[ValidateAntiForgeryToken] | ||
public async Task<ActionResult> Create(CreateSampleDataViewModel model) | ||
{ | ||
if (!ModelState.IsValid) | ||
return View(model); | ||
|
||
int changes; | ||
using (var context = new SampleDataDbContext()) | ||
{ | ||
var sampleData = new SampleData { Name = model.Name }; | ||
context.Set<SampleData>().Add(sampleData); | ||
changes = await context.SaveChangesAsync(); | ||
} | ||
|
||
AddAlert(new SuccessAlert("Sample added", $"{changes} sample was saved to the database")); | ||
return RedirectToAction("Index"); | ||
} | ||
|
||
// TODO: make this a web api controller action | ||
/// <summary> | ||
/// Generates the given count of sample data, and calls the bulk action to insert into the database | ||
/// </summary> | ||
/// <remarks> | ||
/// This intentionally makes an async HTTP call to bulk insert. | ||
/// </remarks> | ||
/// <param name="count">The count of sample data to generate</param> | ||
[HttpPost] | ||
public async Task<ActionResult> Generate(int count) | ||
{ | ||
if (!ModelState.IsValid) | ||
return JsonBadRequest(new { success = false, message = "Invalid samples" }); | ||
|
||
if (count <= 0) | ||
return JsonBadRequest(new { success = false, message = "count must be greater than 0" }); | ||
|
||
int existingCount; | ||
using (var context = new SampleDataDbContext()) | ||
existingCount = await context.Set<SampleData>().CountAsync(); | ||
|
||
var samples = Enumerable.Range(existingCount, count) | ||
.Select(i => new CreateSampleDataViewModel { Name = $"Generated sample {i}" }); | ||
|
||
var client = new HttpClient(); | ||
var bulkUrl = Url.Action("Bulk", "Database", null, Request.Url.Scheme); | ||
var json = JsonConvert.SerializeObject(samples); | ||
var contentType = "application/json"; | ||
var content = new StringContent(json, Encoding.UTF8, contentType); | ||
|
||
var response = await client.PostAsync(bulkUrl, content).ConfigureAwait(false); | ||
var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); | ||
|
||
return Stream(responseStream, contentType, (int)response.StatusCode); | ||
} | ||
|
||
// TODO: make this a web api controller action | ||
/// <summary> | ||
/// Bulk inserts sample data into the database | ||
/// </summary> | ||
/// <param name="model">The sample data to insert</param> | ||
[HttpPost] | ||
public async Task<ActionResult> Bulk(IEnumerable<CreateSampleDataViewModel> model) | ||
{ | ||
if (!ModelState.IsValid) | ||
return JsonBadRequest(new { success = false, message = "Invalid samples" }); | ||
|
||
var sampleData = model.Select(m => new SampleData { Name = m.Name }); | ||
int changes; | ||
|
||
using (var context = new SampleDataDbContext()) | ||
using (var transaction = context.Database.BeginTransaction()) | ||
{ | ||
context.Configuration.AutoDetectChangesEnabled = false; | ||
context.Configuration.ValidateOnSaveEnabled = false; | ||
context.Set<SampleData>().AddRange(sampleData); | ||
changes = await context.SaveChangesAsync(); | ||
transaction.Commit(); | ||
} | ||
|
||
return Json(new { success = true, changes }); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
15 changes: 15 additions & 0 deletions
15
sample/AspNetFullFrameworkSampleApp/Models/CreateSampleDataViewModel.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// Licensed to Elasticsearch B.V under | ||
// one or more agreements. | ||
// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. | ||
// See the LICENSE file in the project root for more information | ||
|
||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace AspNetFullFrameworkSampleApp.Models | ||
{ | ||
public class CreateSampleDataViewModel | ||
{ | ||
[Required] | ||
public string Name { get; set; } | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
sample/AspNetFullFrameworkSampleApp/Mvc/JsonBadRequestResult.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
using System.Web.Mvc; | ||
|
||
namespace AspNetFullFrameworkSampleApp.Mvc | ||
{ | ||
public class JsonBadRequestResult : JsonResult | ||
{ | ||
public override void ExecuteResult(ControllerContext context) | ||
{ | ||
context.RequestContext.HttpContext.Response.StatusCode = 400; | ||
base.ExecuteResult(context); | ||
} | ||
} | ||
} |
Oops, something went wrong.