Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.28.0 #130

Merged
merged 2 commits into from
Nov 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading