Skip to content

Commit

Permalink
v3.28.0 (#130)
Browse files Browse the repository at this point in the history
* v3.28.0
- *Enhancement:* Added extended capabilities to the `InvokeArgs` to allow additional customization.

* Fix failing test.
  • Loading branch information
chullybun authored Nov 9, 2024
1 parent 1dfcbda commit 17aabf8
Show file tree
Hide file tree
Showing 10 changed files with 187 additions and 64 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

Represents the **NuGet** versions.

## v3.28.0
- *Enhancement:* Added extended capabilities to the `InvokeArgs` to allow additional customization.

## v3.27.3
- *Fixed:* The `ExecutionContext.Messages` were not being returned as intended within the `x-messages` HTTP Response header; enabled within the `ExtendedStatusCodeResult` and `ExtendedContentResult` on success only (status code `>= 200` and `<= 299`). Note these messages are JSON serialized as the underlying `MessageItemCollection` type.
- *Fixed:* The `AgentTester` has been updated to return a `HttpResultAssertor` where the operation returns a `HttpResult` to enable further assertions to be made on the `Result` itself.
Expand Down
2 changes: 1 addition & 1 deletion Common.targets
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<Project>
<PropertyGroup>
<Version>3.27.3</Version>
<Version>3.28.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
25 changes: 25 additions & 0 deletions src/CoreEx/Abstractions/Internal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using Microsoft.Extensions.Caching.Memory;

namespace CoreEx.Abstractions
{
/// <summary>
/// Provides shareable internal capabilities.
/// </summary>
/// <remarks>This is intended for internal usage only; use with caution.</remarks>
public static class Internal
{
private static IMemoryCache? _fallbackCache;

/// <summary>
/// Gets the <b>CoreEx</b> fallback <see cref="IMemoryCache"/>.
/// </summary>
public static IMemoryCache MemoryCache => ExecutionContext.GetService<IInternalCache>() ?? (_fallbackCache ??= new MemoryCache(new MemoryCacheOptions()));

/// <summary>
/// Represents a cache for internal capabilities.
/// </summary>
public interface IInternalCache : IMemoryCache { }
}
}
4 changes: 1 addition & 3 deletions src/CoreEx/Abstractions/Reflection/PropertyExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,10 @@ namespace CoreEx.Abstractions.Reflection
/// </summary>
public static partial class PropertyExpression
{
private static IMemoryCache? _fallbackCache;

/// <summary>
/// Gets the <see cref="IMemoryCache"/>.
/// </summary>
internal static IMemoryCache Cache => ExecutionContext.GetService<IReflectionCache>() ?? (_fallbackCache ??= new MemoryCache(new MemoryCacheOptions()));
internal static IMemoryCache Cache => ExecutionContext.GetService<IReflectionCache>() ?? Internal.MemoryCache;

/// <summary>
/// Gets or sets the <see cref="IMemoryCache"/> absolute expiration <see cref="TimeSpan"/>.
Expand Down
34 changes: 34 additions & 0 deletions src/CoreEx/Invokers/IInvoker.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using System.Diagnostics;
using System;
using Microsoft.Extensions.Logging;

namespace CoreEx.Invokers
{
/// <summary>
/// Enables the standardized invoker capabilities.
/// </summary>
public interface IInvoker
{
/// <summary>
/// Gets the start of an <see cref="Activity"/> action.
/// </summary>
Action<InvokeArgs>? OnActivityStart { get; }

/// <summary>
/// Gets the <see cref="Activity"/> <see cref="Exception"/> action.
/// </summary>
Action<InvokeArgs, Exception>? OnActivityException { get; }

/// <summary>
/// Gets the completion of an <see cref="Activity"/> action.
/// </summary>
Action<InvokeArgs>? OnActivityComplete { get; }

/// <summary>
/// Get the caller information <see cref="ILogger"/> formatter.
/// </summary>
Func<InvokeArgs, string> CallerLoggerFormatter { get; }
}
}
137 changes: 87 additions & 50 deletions src/CoreEx/Invokers/InvokeArgs.cs

Large diffs are not rendered by default.

19 changes: 16 additions & 3 deletions src/CoreEx/Invokers/InvokerBaseT.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -13,8 +14,20 @@ namespace CoreEx.Invokers
/// <typeparam name="TInvoker">The owner (invoking) <see cref="Type"/>.</typeparam>
/// <remarks>All public methods result in either the synchronous <see cref="OnInvoke"/> or asynchronous <see cref="OnInvokeAsync"/>virtual methods being called to manage the underlying invocation; therefore, where overridding each should
/// be overridden with the same logic. Where no result is specified this defaults to '<c>object?</c>' for the purposes of execution.</remarks>
public abstract class InvokerBase<TInvoker>
public abstract class InvokerBase<TInvoker> : IInvoker
{
/// <inheritdoc/>
public Action<InvokeArgs>? OnActivityStart { get; protected set; }

/// <inheritdoc/>
public Action<InvokeArgs, Exception>? OnActivityException { get; protected set; }

/// <inheritdoc/>
public Action<InvokeArgs>? OnActivityComplete { get; protected set; }

/// <inheritdoc/>
public Func<InvokeArgs, string> CallerLoggerFormatter { get; protected set; } = InvokeArgs.DefaultCallerLogFormatter;

/// <summary>
/// Invokes a <paramref name="func"/> with a <typeparamref name="TResult"/> synchronously.
/// </summary>
Expand All @@ -41,7 +54,7 @@ public abstract class InvokerBase<TInvoker>
/// </summary>
private TResult TraceOnInvoke<TResult>(TInvoker invoker, Func<InvokeArgs, TResult> func, string? memberName)
{
var ia = new InvokeArgs(GetType(), invoker?.GetType(), memberName, null);
var ia = new InvokeArgs(this, invoker, memberName, null);
try
{
return ia.TraceResult(OnInvoke(ia, invoker, func));
Expand All @@ -62,7 +75,7 @@ private TResult TraceOnInvoke<TResult>(TInvoker invoker, Func<InvokeArgs, TResul
/// </summary>
private async Task<TResult> TraceOnInvokeAsync<TResult>(TInvoker invoker, Func<InvokeArgs, CancellationToken, Task<TResult>> func, string? memberName, CancellationToken cancellationToken)
{
var ia = new InvokeArgs(GetType(), invoker?.GetType(), memberName, null);
var ia = new InvokeArgs(this, invoker, memberName, null);
try
{
return ia.TraceResult(await OnInvokeAsync(ia, invoker, func, cancellationToken).ConfigureAwait(false));
Expand Down
19 changes: 16 additions & 3 deletions src/CoreEx/Invokers/InvokerBaseT2.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx

using System;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,8 +15,20 @@ namespace CoreEx.Invokers
/// <typeparam name="TArgs">The arguments <see cref="Type"/>.</typeparam>
/// <remarks>All public methods result in either the synchronous <see cref="OnInvoke"/> or asynchronous <see cref="OnInvokeAsync"/> virtual methods being called to manage the underlying invocation; therefore, where overridding each should
/// be overridden with the same logic. Where no result is specified this defaults to '<c>object?</c>' for the purposes of execution.</remarks>
public abstract class InvokerBase<TInvoker, TArgs>
public abstract class InvokerBase<TInvoker, TArgs> : IInvoker
{
/// <inheritdoc/>
public Action<InvokeArgs>? OnActivityStart { get; protected set; }

/// <inheritdoc/>
public Action<InvokeArgs, Exception>? OnActivityException { get; protected set; }

/// <inheritdoc/>
public Action<InvokeArgs>? OnActivityComplete { get; protected set; }

/// <inheritdoc/>
public Func<InvokeArgs, string> CallerLoggerFormatter { get; protected set; } = InvokeArgs.DefaultCallerLogFormatter;

/// <summary>
/// Invokes a <paramref name="func"/> with a <typeparamref name="TResult"/> synchronously.
/// </summary>
Expand Down Expand Up @@ -44,7 +57,7 @@ public abstract class InvokerBase<TInvoker, TArgs>
/// </summary>
private TResult TraceOnInvoke<TResult>(TInvoker invoker, Func<InvokeArgs, TResult> func, TArgs? args, string? memberName)
{
var ia = new InvokeArgs(GetType(), invoker?.GetType(), memberName, null);
var ia = new InvokeArgs(this, invoker, memberName, null);
try
{
return ia.TraceResult(OnInvoke(ia, invoker, func, args));
Expand All @@ -65,7 +78,7 @@ private TResult TraceOnInvoke<TResult>(TInvoker invoker, Func<InvokeArgs, TResul
/// </summary>
private async Task<TResult> TraceOnInvokeAsync<TResult>(TInvoker invoker, Func<InvokeArgs, CancellationToken, Task<TResult>> func, TArgs? args, string? memberName, CancellationToken cancellationToken)
{
var ia = new InvokeArgs(GetType(), invoker?.GetType(), memberName, null);
var ia = new InvokeArgs(this, invoker, memberName, null);
try
{
return ia.TraceResult(await OnInvokeAsync(ia, invoker, func, args, cancellationToken).ConfigureAwait(false));
Expand Down
6 changes: 3 additions & 3 deletions src/CoreEx/RefData/ReferenceDataOrchestrator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,7 @@ public ReferenceDataOrchestrator Register<TProvider>() where TProvider : IRefere

Logger.LogDebug("Reference data type {RefDataType} cache load start: ServiceProvider.CreateScope and Threading.ExecutionContext.SuppressFlow to support underlying cache data get.", type.FullName);
using var ec = ExecutionContext.Current.CreateCopy();
var rdo = _asyncLocal.Value;
var rdo = this;

using var scope = ServiceProvider.CreateScope();
Task<IReferenceDataCollection> task;
Expand All @@ -301,15 +301,15 @@ public ReferenceDataOrchestrator Register<TProvider>() where TProvider : IRefere
/// <summary>
/// Performs the actual reference data load in a new thread context / scope.
/// </summary>
private async Task<IReferenceDataCollection> GetByTypeInNewScopeAsync(ReferenceDataOrchestrator? rdo, ExecutionContext executionContext, IServiceScope scope, Type type, Type providerType, InvokeArgs invokeArgs, CancellationToken cancellationToken)
private async Task<IReferenceDataCollection> GetByTypeInNewScopeAsync(ReferenceDataOrchestrator rdo, ExecutionContext executionContext, IServiceScope scope, Type type, Type providerType, InvokeArgs invokeArgs, CancellationToken cancellationToken)
{
_asyncLocal.Value = rdo;

executionContext.ServiceProvider = scope.ServiceProvider;
ExecutionContext.SetCurrent(executionContext);

// Start related activity as this "work" is occuring on an unrelated different thread (by design to ensure complete separation).
var ria = invokeArgs.StartNewRelated(typeof(ReferenceDataOrchestratorInvoker), typeof(ReferenceDataOrchestrator), nameof(GetByTypeInNewScopeAsync));
var ria = invokeArgs.StartNewRelated(invokeArgs.Invoker, rdo, nameof(GetByTypeInNewScopeAsync));
try
{
if (ria.Activity is not null)
Expand Down
2 changes: 1 addition & 1 deletion tests/CoreEx.Test/Framework/Invokers/InvokerBaseTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ public void Invoke_AsyncWithResult()
public async Task Invoke_AsyncWithResult_Load()
{
var i = new TestInvoker();
for (var j = 0; j < 1000; j++)
for (var j = 0; j < 100000; j++)
{
await i.InvokeAsync(this, async (_, ct) => { await Task.Delay(0, ct); return 88; });
}
Expand Down

0 comments on commit 17aabf8

Please sign in to comment.