Skip to content

Commit aac3085

Browse files
author
Bart Koelman
authored
Merge 3904223 into d555bb5
2 parents d555bb5 + 3904223 commit aac3085

File tree

47 files changed

+936
-163
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+936
-163
lines changed

JsonApiDotNetCore.sln.DotSettings

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,10 @@ JsonApiDotNetCore.ArgumentGuard.NotNull($EXPR$, $NAME$);</s:String>
7878
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TailRecursiveCall/@EntryIndexedValue">SUGGESTION</s:String>
7979
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=TryCastAlwaysSucceeds/@EntryIndexedValue">WARNING</s:String>
8080
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UnnecessaryWhitespace/@EntryIndexedValue">HINT</s:String>
81+
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseArrayEmptyMethod/@EntryIndexedValue">WARNING</s:String>
8182
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseCollectionCountProperty/@EntryIndexedValue">WARNING</s:String>
83+
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseEmptyTypesField/@EntryIndexedValue">WARNING</s:String>
84+
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=UseEventArgsEmptyField/@EntryIndexedValue">WARNING</s:String>
8285
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=VirtualMemberNeverOverridden_002ELocal/@EntryIndexedValue">WARNING</s:String>
8386
<s:String x:Key="/Default/CodeStyle/CodeCleanup/Profiles/=JADNC_0020Full_0020Cleanup/@EntryIndexedValue">&lt;?xml version="1.0" encoding="utf-16"?&gt;&lt;Profile name="JADNC Full Cleanup"&gt;&lt;XMLReformatCode&gt;True&lt;/XMLReformatCode&gt;&lt;CSCodeStyleAttributes ArrangeTypeAccessModifier="True" ArrangeTypeMemberAccessModifier="True" SortModifiers="True" RemoveRedundantParentheses="True" AddMissingParentheses="True" ArrangeBraces="True" ArrangeAttributes="True" ArrangeArgumentsStyle="True" ArrangeCodeBodyStyle="True" ArrangeVarStyle="True" ArrangeTrailingCommas="True" ArrangeObjectCreation="True" ArrangeDefaultValue="True" /&gt;&lt;CssAlphabetizeProperties&gt;True&lt;/CssAlphabetizeProperties&gt;&lt;JsInsertSemicolon&gt;True&lt;/JsInsertSemicolon&gt;&lt;FormatAttributeQuoteDescriptor&gt;True&lt;/FormatAttributeQuoteDescriptor&gt;&lt;CorrectVariableKindsDescriptor&gt;True&lt;/CorrectVariableKindsDescriptor&gt;&lt;VariablesToInnerScopesDescriptor&gt;True&lt;/VariablesToInnerScopesDescriptor&gt;&lt;StringToTemplatesDescriptor&gt;True&lt;/StringToTemplatesDescriptor&gt;&lt;JsReformatCode&gt;True&lt;/JsReformatCode&gt;&lt;JsFormatDocComments&gt;True&lt;/JsFormatDocComments&gt;&lt;RemoveRedundantQualifiersTs&gt;True&lt;/RemoveRedundantQualifiersTs&gt;&lt;OptimizeImportsTs&gt;True&lt;/OptimizeImportsTs&gt;&lt;OptimizeReferenceCommentsTs&gt;True&lt;/OptimizeReferenceCommentsTs&gt;&lt;PublicModifierStyleTs&gt;True&lt;/PublicModifierStyleTs&gt;&lt;ExplicitAnyTs&gt;True&lt;/ExplicitAnyTs&gt;&lt;TypeAnnotationStyleTs&gt;True&lt;/TypeAnnotationStyleTs&gt;&lt;RelativePathStyleTs&gt;True&lt;/RelativePathStyleTs&gt;&lt;AsInsteadOfCastTs&gt;True&lt;/AsInsteadOfCastTs&gt;&lt;HtmlReformatCode&gt;True&lt;/HtmlReformatCode&gt;&lt;AspOptimizeRegisterDirectives&gt;True&lt;/AspOptimizeRegisterDirectives&gt;&lt;RemoveCodeRedundancies&gt;True&lt;/RemoveCodeRedundancies&gt;&lt;CSUseAutoProperty&gt;True&lt;/CSUseAutoProperty&gt;&lt;CSMakeFieldReadonly&gt;True&lt;/CSMakeFieldReadonly&gt;&lt;CSMakeAutoPropertyGetOnly&gt;True&lt;/CSMakeAutoPropertyGetOnly&gt;&lt;CSArrangeQualifiers&gt;True&lt;/CSArrangeQualifiers&gt;&lt;CSFixBuiltinTypeReferences&gt;True&lt;/CSFixBuiltinTypeReferences&gt;&lt;CssReformatCode&gt;True&lt;/CssReformatCode&gt;&lt;CSOptimizeUsings&gt;&lt;OptimizeUsings&gt;True&lt;/OptimizeUsings&gt;&lt;EmbraceInRegion&gt;False&lt;/EmbraceInRegion&gt;&lt;RegionName&gt;&lt;/RegionName&gt;&lt;/CSOptimizeUsings&gt;&lt;CSShortenReferences&gt;True&lt;/CSShortenReferences&gt;&lt;CSReformatCode&gt;True&lt;/CSReformatCode&gt;&lt;CSharpFormatDocComments&gt;True&lt;/CSharpFormatDocComments&gt;&lt;CSReorderTypeMembers&gt;True&lt;/CSReorderTypeMembers&gt;&lt;XAMLCollapseEmptyTags&gt;False&lt;/XAMLCollapseEmptyTags&gt;&lt;/Profile&gt;</s:String>
8487
<s:String x:Key="/Default/CodeStyle/CodeCleanup/SilentCleanupProfile/@EntryValue">JADNC Full Cleanup</s:String>
@@ -620,15 +623,19 @@ $left$ = $right$;</s:String>
620623
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=B3D9EE6B4EC62A4F961EB15F9ADEC2C6/ReplacePattern/@EntryValue">$collection$.IsNullOrEmpty()</s:String>
621624
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=B3D9EE6B4EC62A4F961EB15F9ADEC2C6/SearchPattern/@EntryValue">$collection$ == null || !$collection$.Any()</s:String>
622625
<s:String x:Key="/Default/PatternsAndTemplates/StructuralSearch/Pattern/=B3D9EE6B4EC62A4F961EB15F9ADEC2C6/Severity/@EntryValue">WARNING</s:String>
626+
<s:Boolean x:Key="/Default/UserDictionary/Words/=appsettings/@EntryIndexedValue">True</s:Boolean>
623627
<s:Boolean x:Key="/Default/UserDictionary/Words/=Assignee/@EntryIndexedValue">True</s:Boolean>
624628
<s:Boolean x:Key="/Default/UserDictionary/Words/=Injectables/@EntryIndexedValue">True</s:Boolean>
629+
<s:Boolean x:Key="/Default/UserDictionary/Words/=jsonapi/@EntryIndexedValue">True</s:Boolean>
625630
<s:Boolean x:Key="/Default/UserDictionary/Words/=linebreaks/@EntryIndexedValue">True</s:Boolean>
626631
<s:Boolean x:Key="/Default/UserDictionary/Words/=Microservices/@EntryIndexedValue">True</s:Boolean>
627632
<s:Boolean x:Key="/Default/UserDictionary/Words/=navigations/@EntryIndexedValue">True</s:Boolean>
633+
<s:Boolean x:Key="/Default/UserDictionary/Words/=parallelize/@EntryIndexedValue">True</s:Boolean>
628634
<s:Boolean x:Key="/Default/UserDictionary/Words/=playlists/@EntryIndexedValue">True</s:Boolean>
629635
<s:Boolean x:Key="/Default/UserDictionary/Words/=Rewriter/@EntryIndexedValue">True</s:Boolean>
630636
<s:Boolean x:Key="/Default/UserDictionary/Words/=Startups/@EntryIndexedValue">True</s:Boolean>
631637
<s:Boolean x:Key="/Default/UserDictionary/Words/=subdirectory/@EntryIndexedValue">True</s:Boolean>
632638
<s:Boolean x:Key="/Default/UserDictionary/Words/=unarchive/@EntryIndexedValue">True</s:Boolean>
633639
<s:Boolean x:Key="/Default/UserDictionary/Words/=Workflows/@EntryIndexedValue">True</s:Boolean>
640+
<s:Boolean x:Key="/Default/UserDictionary/Words/=xunit/@EntryIndexedValue">True</s:Boolean>
634641
</wpf:ResourceDictionary>

docs/docfx.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,7 @@
88
}
99
],
1010
"dest": "api",
11-
"disableGitFeatures": false,
12-
"properties": {
13-
"targetFramework": "netcoreapp3.1"
14-
}
11+
"disableGitFeatures": false
1512
}
1613
],
1714
"build": {

src/Examples/JsonApiDotNetCoreExample/Startups/EmptyStartup.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using Microsoft.AspNetCore.Builder;
22
using Microsoft.AspNetCore.Hosting;
33
using Microsoft.Extensions.DependencyInjection;
4+
using Microsoft.Extensions.Logging;
45

56
namespace JsonApiDotNetCoreExample.Startups
67
{
@@ -14,7 +15,9 @@ public virtual void ConfigureServices(IServiceCollection services)
1415
{
1516
}
1617

17-
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
18+
// ReSharper disable once UnusedMemberInSuper.Global
19+
// ReSharper disable once UnusedParameter.Global
20+
public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment environment, ILoggerFactory loggerFactory)
1821
{
1922
}
2023
}
Lines changed: 54 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,67 +1,97 @@
11
using System;
22
using JsonApiDotNetCore.Configuration;
3+
using JsonApiDotNetCore.Diagnostics;
34
using JsonApiDotNetCoreExample.Data;
45
using Microsoft.AspNetCore.Authentication;
56
using Microsoft.AspNetCore.Builder;
67
using Microsoft.AspNetCore.Hosting;
78
using Microsoft.EntityFrameworkCore;
89
using Microsoft.Extensions.Configuration;
910
using Microsoft.Extensions.DependencyInjection;
11+
using Microsoft.Extensions.Logging;
1012
using Newtonsoft.Json;
1113
using Newtonsoft.Json.Converters;
1214

1315
namespace JsonApiDotNetCoreExample.Startups
1416
{
1517
public sealed class Startup : EmptyStartup
1618
{
19+
private readonly ICodeTimerSession _codeTimingSession;
1720
private readonly string _connectionString;
1821

1922
public Startup(IConfiguration configuration)
2023
{
24+
_codeTimingSession = new DefaultCodeTimerSession();
25+
CodeTimingSessionManager.Capture(_codeTimingSession);
26+
2127
string postgresPassword = Environment.GetEnvironmentVariable("PGPASSWORD") ?? "postgres";
2228
_connectionString = configuration["Data:DefaultConnection"].Replace("###", postgresPassword);
2329
}
2430

2531
// This method gets called by the runtime. Use this method to add services to the container.
2632
public override void ConfigureServices(IServiceCollection services)
2733
{
28-
services.AddSingleton<ISystemClock, SystemClock>();
29-
30-
services.AddDbContext<AppDbContext>(options =>
34+
using (CodeTimingSessionManager.Current.Measure("Configure other (startup)"))
3135
{
32-
options.UseNpgsql(_connectionString);
36+
services.AddSingleton<ISystemClock, SystemClock>();
37+
38+
services.AddDbContext<AppDbContext>(options =>
39+
{
40+
options.UseNpgsql(_connectionString);
3341
#if DEBUG
34-
options.EnableSensitiveDataLogging();
35-
options.EnableDetailedErrors();
42+
options.EnableSensitiveDataLogging();
43+
options.EnableDetailedErrors();
3644
#endif
37-
});
45+
});
3846

39-
services.AddJsonApi<AppDbContext>(options =>
40-
{
41-
options.Namespace = "api/v1";
42-
options.UseRelativeLinks = true;
43-
options.ValidateModelState = true;
44-
options.IncludeTotalResourceCount = true;
45-
options.SerializerSettings.Formatting = Formatting.Indented;
46-
options.SerializerSettings.Converters.Add(new StringEnumConverter());
47+
using (CodeTimingSessionManager.Current.Measure("Configure JSON:API (startup)"))
48+
{
49+
services.AddJsonApi<AppDbContext>(options =>
50+
{
51+
options.Namespace = "api/v1";
52+
options.UseRelativeLinks = true;
53+
options.ValidateModelState = true;
54+
options.IncludeTotalResourceCount = true;
55+
options.SerializerSettings.Formatting = Formatting.Indented;
56+
options.SerializerSettings.Converters.Add(new StringEnumConverter());
4757
#if DEBUG
48-
options.IncludeExceptionStackTraceInErrors = true;
58+
options.IncludeExceptionStackTraceInErrors = true;
4959
#endif
50-
}, discovery => discovery.AddCurrentAssembly());
60+
}, discovery => discovery.AddCurrentAssembly());
61+
}
62+
}
5163
}
5264

5365
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
54-
public override void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
66+
public override void Configure(IApplicationBuilder app, IWebHostEnvironment environment, ILoggerFactory loggerFactory)
5567
{
56-
using (IServiceScope scope = app.ApplicationServices.CreateScope())
68+
ILogger<Startup> logger = loggerFactory.CreateLogger<Startup>();
69+
70+
using (CodeTimingSessionManager.Current.Measure("Initialize other (startup)"))
71+
{
72+
using (IServiceScope scope = app.ApplicationServices.CreateScope())
73+
{
74+
var appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
75+
appDbContext.Database.EnsureCreated();
76+
}
77+
78+
app.UseRouting();
79+
80+
using (CodeTimingSessionManager.Current.Measure("Initialize JSON:API (startup)"))
81+
{
82+
app.UseJsonApi();
83+
}
84+
85+
app.UseEndpoints(endpoints => endpoints.MapControllers());
86+
}
87+
88+
if (CodeTimingSessionManager.IsEnabled)
5789
{
58-
var appDbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
59-
appDbContext.Database.EnsureCreated();
90+
string timingResults = CodeTimingSessionManager.Current.GetResults();
91+
logger.LogInformation($"Measurement results for application startup:{Environment.NewLine}{timingResults}");
6092
}
6193

62-
app.UseRouting();
63-
app.UseJsonApi();
64-
app.UseEndpoints(endpoints => endpoints.MapControllers());
94+
_codeTimingSession.Dispose();
6595
}
6696
}
6797
}

src/Examples/JsonApiDotNetCoreExample/appsettings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@
77
"Default": "Warning",
88
"Microsoft.Hosting.Lifetime": "Warning",
99
"Microsoft.EntityFrameworkCore.Update": "Critical",
10-
"Microsoft.EntityFrameworkCore.Database.Command": "Critical"
10+
"Microsoft.EntityFrameworkCore.Database.Command": "Critical",
11+
"JsonApiDotNetCore.Middleware.JsonApiMiddleware": "Information",
12+
"JsonApiDotNetCoreExample": "Information"
1113
}
1214
},
1315
"AllowedHosts": "*"
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
using System;
2+
using JetBrains.Annotations;
3+
using Microsoft.AspNetCore.Http;
4+
5+
namespace JsonApiDotNetCore.Diagnostics
6+
{
7+
/// <summary>
8+
/// Code timing session management intended for use in ASP.NET Web Applications. Uses <see cref="HttpContext.Items" /> to isolate concurrent requests.
9+
/// Can be used with async/wait, but it cannot distinguish between concurrently running threads within a single HTTP request, so you'll need to pass an
10+
/// <see cref="CascadingCodeTimer" /> instance through the entire call chain in that case.
11+
/// </summary>
12+
[PublicAPI]
13+
public sealed class AspNetCodeTimerSession : ICodeTimerSession
14+
{
15+
private const string HttpContextItemKey = "CascadingCodeTimer:Session";
16+
17+
private readonly HttpContext _httpContext;
18+
private readonly IHttpContextAccessor _httpContextAccessor;
19+
20+
public ICodeTimer CodeTimer
21+
{
22+
get
23+
{
24+
HttpContext httpContext = GetHttpContext();
25+
var codeTimer = (ICodeTimer)httpContext.Items[HttpContextItemKey];
26+
27+
if (codeTimer == null)
28+
{
29+
codeTimer = new CascadingCodeTimer();
30+
httpContext.Items[HttpContextItemKey] = codeTimer;
31+
}
32+
33+
return codeTimer;
34+
}
35+
}
36+
37+
public event EventHandler Disposed;
38+
39+
public AspNetCodeTimerSession(IHttpContextAccessor httpContextAccessor)
40+
{
41+
ArgumentGuard.NotNull(httpContextAccessor, nameof(httpContextAccessor));
42+
43+
_httpContextAccessor = httpContextAccessor;
44+
}
45+
46+
public AspNetCodeTimerSession(HttpContext httpContext)
47+
{
48+
ArgumentGuard.NotNull(httpContext, nameof(httpContext));
49+
50+
_httpContext = httpContext;
51+
}
52+
53+
public void Dispose()
54+
{
55+
HttpContext httpContext = TryGetHttpContext();
56+
var codeTimer = (ICodeTimer)httpContext?.Items[HttpContextItemKey];
57+
58+
if (codeTimer != null)
59+
{
60+
codeTimer.Dispose();
61+
httpContext.Items[HttpContextItemKey] = null;
62+
}
63+
64+
OnDisposed();
65+
}
66+
67+
private void OnDisposed()
68+
{
69+
Disposed?.Invoke(this, EventArgs.Empty);
70+
}
71+
72+
private HttpContext GetHttpContext()
73+
{
74+
HttpContext httpContext = TryGetHttpContext();
75+
return httpContext ?? throw new InvalidOperationException("An active HTTP request is required.");
76+
}
77+
78+
private HttpContext TryGetHttpContext()
79+
{
80+
return _httpContext ?? _httpContextAccessor?.HttpContext;
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)