Skip to content

Commit

Permalink
v3.27.2 (#127)
Browse files Browse the repository at this point in the history
- *Fixed:* The `IServiceCollection.AddCosmosDb` extension method was registering as a singleton; this has been corrected to register as scoped. The dependent `CosmosClient` should remain a singleton as is [best practice](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/best-practice-dotnet).
  • Loading branch information
chullybun authored Oct 17, 2024
1 parent 26ecd3a commit ce9f2dc
Show file tree
Hide file tree
Showing 9 changed files with 34 additions and 12 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.27.2
- *Fixed:* The `IServiceCollection.AddCosmosDb` extension method was registering as a singleton; this has been corrected to register as scoped. The dependent `CosmosClient` should remain a singleton as is [best practice](https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/best-practice-dotnet).

## v3.27.1
- *Fixed:* Updated `Microsoft.Extensions.Caching.Memory` package depenedency to latest (including related); resolve [Microsoft Security Advisory CVE-2024-43483](https://github.com/advisories/GHSA-qj66-m88j-hmgj).
- *Fixed:* Fixed the `ExecutionContext.UserIsAuthorized` to have base implementation similar to `UserIsInRole`.
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.1</Version>
<Version>3.27.2</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public void ConfigureServices(IServiceCollection services)
.AddAzureServiceBusSender()
.AddAzureServiceBusPurger()
.AddJsonMergePatch()
.AddWebApi((_, c) => c.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? WebApiBase.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddReferenceDataContentWebApi()
.AddRequestCache();

Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.Functions/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ public override void Configure(IFunctionsHostBuilder builder)
.AddEventPublisher()
.AddSingleton(sp => new Az.ServiceBusClient(sp.GetRequiredService<HrSettings>().ServiceBusConnection__fullyQualifiedNamespace))
.AddAzureServiceBusSender()
.AddWebApi((_, c) => c.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? WebApiBase.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddWebApi((_, webapi) => webapi.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? webapi.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddJsonMergePatch()
.AddWebApiPublisher()
.AddAzureServiceBusSubscriber();
Expand Down
19 changes: 17 additions & 2 deletions src/CoreEx.AspNetCore/WebApis/WebApiBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,12 @@ public abstract class WebApiBase(ExecutionContext executionContext, SettingsBase
/// <remarks>Searches the <see cref="HttpRequest.Headers"/> for <see cref="HttpConsts.CorrelationIdHeaderName"/> or one of the other <see cref="SecondaryCorrelationIdNames"/> to determine the <see cref="ExecutionContext.CorrelationId"/> (uses first value found in sequence).</remarks>
public IEnumerable<string> SecondaryCorrelationIdNames { get; set; } = ["x-ms-client-tracking-id"];

/// <summary>
/// Gets or sets the <see cref="IExtendedException"/> <see cref="IActionResult"/> creator function used by <see cref="CreateActionResultFromExtendedException(IExtendedException)"/>.
/// </summary>
/// <remarks>This allows an alternate serialization or handling as required. Defaults to the <see cref="DefaultExtendedExceptionActionResultCreator"/>.</remarks>
public Func<IExtendedException, IActionResult> ExtendedExceptionActionResultCreator { get; set; } = DefaultExtendedExceptionActionResultCreator;

/// <summary>
/// Gets the list of correlation identifier names, being <see cref="HttpConsts.CorrelationIdHeaderName"/> and <see cref="SecondaryCorrelationIdNames"/> (inclusive).
/// </summary>
Expand Down Expand Up @@ -183,7 +189,9 @@ public static async Task<IActionResult> CreateActionResultFromExceptionAsync(Web
if (eex.ShouldBeLogged)
logger.LogError(exception, "{Error}", exception.Message);

ar = CreateActionResultFromExtendedException(eex);
ar = owner is null
? DefaultExtendedExceptionActionResultCreator(eex)
: owner.CreateActionResultFromExtendedException(eex);
}
else
{
Expand All @@ -204,7 +212,14 @@ public static async Task<IActionResult> CreateActionResultFromExceptionAsync(Web
/// Creates an <see cref="IActionResult"/> from an <paramref name="extendedException"/>.
/// </summary>
/// <param name="extendedException">The <see cref="IExtendedException"/>.</param>
public static IActionResult CreateActionResultFromExtendedException(IExtendedException extendedException)
public IActionResult CreateActionResultFromExtendedException(IExtendedException extendedException) => ExtendedExceptionActionResultCreator(extendedException);

/// <summary>
/// The default <see cref="ExtendedExceptionActionResultCreator"/>.
/// </summary>
/// <param name="extendedException">The <see cref="IExtendedException"/>.</param>
/// <returns>The resulting <see cref="IActionResult"/>.</returns>
public static IActionResult DefaultExtendedExceptionActionResultCreator(IExtendedException extendedException)
{
if (extendedException is ValidationException vex && vex.Messages is not null && vex.Messages.Count > 0)
{
Expand Down
7 changes: 6 additions & 1 deletion src/CoreEx.Cosmos/CosmosDb.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ namespace CoreEx.Cosmos
/// <param name="database">The <see cref="Microsoft.Azure.Cosmos.Database"/>.</param>
/// <param name="mapper">The <see cref="IMapper"/>.</param>
/// <param name="invoker">Enables the <see cref="Invoker"/> to be overridden; defaults to <see cref="CosmosDbInvoker"/>.</param>
/// <remarks>It is recommended that the <see cref="CosmosDb"/> is registered as a scoped service to enable capabilities such as <see cref="CosmosDbArgs.FilterByTenantId"/> that <i>must</i> be scoped.
/// Use <see cref="Microsoft.Extensions.DependencyInjection.CosmosDbServiceCollectionExtensions.AddCosmosDb{TCosmosDb}(Microsoft.Extensions.DependencyInjection.IServiceCollection, Func{IServiceProvider, TCosmosDb}, string?)"/> to
/// register the scoped <see cref="CosmosDb"/> instance.
/// <para>The dependent <see cref="CosmosClient"/> should however be registered as a singleton as is <see href="https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/best-practice-dotnet">best practice</see>.</para></remarks>
public class CosmosDb(Database database, IMapper mapper, CosmosDbInvoker? invoker = null) : ICosmosDb
{
private static CosmosDbInvoker? _invoker;
Expand Down Expand Up @@ -181,7 +185,8 @@ public async Task<Result> SelectMultiSetWithResultAsync(PartitionKey partitionKe

if (multiSetList.Any(x => !x.Container.IsCosmosDbValueModel))
throw new ArgumentException($"All {nameof(IMultiSetArgs)} containers must be of type CosmosDbValueContainer.", nameof(multiSetArgs));


// Build the Cosmos SQL statement.
var container = multiSetList[0].Container;
var types = new Dictionary<string, IMultiSetArgs>([ new KeyValuePair<string, IMultiSetArgs>(container.ModelType.Name, multiSetList[0]) ]);
var sb = string.IsNullOrEmpty(sql) ? new StringBuilder($"SELECT * FROM c WHERE c.type in (\"{container.ModelType.Name}\"") : null;
Expand Down
8 changes: 4 additions & 4 deletions src/CoreEx.Cosmos/CosmosDbServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static class CosmosDbServiceCollectionExtensions
{
/// <summary>
/// Adds an <see cref="ICosmosDb"/> as a singleton service.
/// Adds an <see cref="ICosmosDb"/> as a scoped service.
/// </summary>
/// <typeparam name="TCosmosDb">The <see cref="ICosmosDb"/> <see cref="Type"/>.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
Expand All @@ -23,15 +23,15 @@ public static class CosmosDbServiceCollectionExtensions
/// <returns>The <see cref="IServiceCollection"/> to support fluent-style method-chaining.</returns>
public static IServiceCollection AddCosmosDb<TCosmosDb>(this IServiceCollection services, Func<IServiceProvider, TCosmosDb> create, bool healthCheck = true) where TCosmosDb : class, ICosmosDb
{
services.ThrowIfNull(nameof(services)).AddSingleton(sp => create.ThrowIfNull(nameof(create)).Invoke(sp));
services.ThrowIfNull(nameof(services)).AddScoped(sp => create.ThrowIfNull(nameof(create)).Invoke(sp));
if (healthCheck)
services.AddHealthChecks().AddCosmosDbHealthCheck<TCosmosDb>();

return services;
}

/// <summary>
/// Adds an <see cref="ICosmosDb"/> as a singleton service including a corresponding health check.
/// Adds an <see cref="ICosmosDb"/> as a scoped service including a corresponding health check.
/// </summary>
/// <typeparam name="TCosmosDb">The <see cref="ICosmosDb"/> <see cref="Type"/>.</typeparam>
/// <param name="services">The <see cref="IServiceCollection"/>.</param>
Expand All @@ -40,7 +40,7 @@ public static IServiceCollection AddCosmosDb<TCosmosDb>(this IServiceCollection
/// <returns>The <see cref="IServiceCollection"/> to support fluent-style method-chaining.</returns>
public static IServiceCollection AddCosmosDb<TCosmosDb>(this IServiceCollection services, Func<IServiceProvider, TCosmosDb> create, string? healthCheckName) where TCosmosDb : class, ICosmosDb
{
services.ThrowIfNull(nameof(services)).AddSingleton(sp => create.ThrowIfNull(nameof(create)).Invoke(sp));
services.ThrowIfNull(nameof(services)).AddScoped(sp => create.ThrowIfNull(nameof(create)).Invoke(sp));
services.AddHealthChecks().AddCosmosDbHealthCheck<TCosmosDb>(healthCheckName);
return services;
}
Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx/Events/EventDataFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ public virtual void Format(EventData @event)
var value = @event.Value;

@event.Id ??= Guid.NewGuid().ToString();
@event.Timestamp ??= new DateTimeOffset(ExecutionContext.SystemTime.UtcNow);
@event.Timestamp ??= new DateTimeOffset(ExecutionContext.HasCurrent ? ExecutionContext.Current.Timestamp : ExecutionContext.SystemTime.UtcNow);

if (PropertySelection.HasFlag(EventDataProperty.Key))
{
Expand Down
1 change: 0 additions & 1 deletion src/CoreEx/ExecutionContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

namespace CoreEx
Expand Down

0 comments on commit ce9f2dc

Please sign in to comment.