Skip to content

Commit

Permalink
v3.12.0 (#91)
Browse files Browse the repository at this point in the history
* PostgreSQL.
Minor fixes.
Internal cleanup.

* Run Code Analysis

* Text.SentenceCase

* Readme doco update.

* JsonDataReader DataOnly/TimeOnly
SentenceCase fix

* README tweak.
  • Loading branch information
chullybun authored Feb 26, 2024
1 parent e683b4c commit 7e45c7e
Show file tree
Hide file tree
Showing 174 changed files with 1,016 additions and 1,275 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,17 @@

Represents the **NuGet** versions.

## v3.12.0
- *Enhancement*: Added new `CoreEx.Database.Postgres` project/package to support [PostgreSQL](https://www.postgresql.org/) database capabilities. Primarily encapsulates the open-source [`Npqsql`](https://www.npgsql.org/) .NET ADO database provider for PostgreSQL.
- Added `EncodedStringToUInt32Converter` to support PostgreSQL `xmin` column encoding as the row version/etag.
- *Enhancement*: Migrated sentence case logic from inside `PropertyExpression` into `CoreEx.Text.SentenceCase` to improve discoverablity and reuse opportunities.
- *Fixed:* The `IServiceCollection.AddAzureServiceBusClient` extension method as been removed; the `ServiceBusClient` will need to be instantiated prior to usage. Standard approach is for consumers to create client instances independently.
- *Fixed*: The `WorkOrchestrator.GetAsync<T>()` and `WorkOrchestrator.GetAsync(string type, ..)` methods were not automatically cancelling where expired.
- *Fixed*: The `InvokerArgs` activity tracing updated to correctly capture the `Exception.Message` where an `Exception` has been thrown.
- *Internal*:
- All `throw new ArgumentNullException` checking migrated to the `xxx.ThrowIfNull` extension method equivalent.
- All _Run Code Analysis_ issues resolved.

## v3.11.0
- *Enhancement*: The `ITypedToResult` updated to correctly implement `IToResult` as the simple `ToResult` where required.
- *Enhancement*: Added `Result.AsTask()` and `Result<T>.AsTask` to simplify the conversion to a completed `Task<Result>` or `Task<Result<T>>` where applicable.
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.11.0</Version>
<Version>3.12.0</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
11 changes: 9 additions & 2 deletions CoreEx.sln
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreEx.UnitTesting", "src\C
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreEx.UnitTesting.NUnit", "src\CoreEx.UnitTesting.NUnit\CoreEx.UnitTesting.NUnit.csproj", "{91910971-4B1A-4791-9BB4-65FAB3ED3C76}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreEx.TestFunctionIso", "tests\CoreEx.TestFunctionIso\CoreEx.TestFunctionIso.csproj", "{6F7B4F1E-3C3A-4CD7-A9BF-973A5053C1C8}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreEx.TestFunctionIso", "tests\CoreEx.TestFunctionIso\CoreEx.TestFunctionIso.csproj", "{6F7B4F1E-3C3A-4CD7-A9BF-973A5053C1C8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreEx.Test2", "tests\CoreEx.Test2\CoreEx.Test2.csproj", "{910B5894-46BC-4427-95D6-2804F06458E3}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CoreEx.Test2", "tests\CoreEx.Test2\CoreEx.Test2.csproj", "{910B5894-46BC-4427-95D6-2804F06458E3}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CoreEx.Database.Postgres", "src\CoreEx.Database.Postgres\CoreEx.Database.Postgres.csproj", "{C042AC2A-415D-432E-83FA-B911FD9ED378}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -209,6 +211,10 @@ Global
{910B5894-46BC-4427-95D6-2804F06458E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
{910B5894-46BC-4427-95D6-2804F06458E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
{910B5894-46BC-4427-95D6-2804F06458E3}.Release|Any CPU.Build.0 = Release|Any CPU
{C042AC2A-415D-432E-83FA-B911FD9ED378}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C042AC2A-415D-432E-83FA-B911FD9ED378}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C042AC2A-415D-432E-83FA-B911FD9ED378}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C042AC2A-415D-432E-83FA-B911FD9ED378}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -245,6 +251,7 @@ Global
{91910971-4B1A-4791-9BB4-65FAB3ED3C76} = {D2C61D4A-2A6D-4284-BF9D-09F51BA735B8}
{6F7B4F1E-3C3A-4CD7-A9BF-973A5053C1C8} = {3145DCB9-98FB-4519-BCC0-75A22A252EDC}
{910B5894-46BC-4427-95D6-2804F06458E3} = {3145DCB9-98FB-4519-BCC0-75A22A252EDC}
{C042AC2A-415D-432E-83FA-B911FD9ED378} = {4B6BC31E-93B1-42B0-AE09-AD85AC4DB657}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B4566D2-9B22-4E27-9654-402BDBA6C744}
Expand Down
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ Package | Status | Source & documentation
`CoreEx.Azure` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Azure.svg)](https://badge.fury.io/nu/CoreEx.Azure) | [Link](./src/CoreEx.Azure)
`CoreEx.Cosmos` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Cosmos.svg)](https://badge.fury.io/nu/CoreEx.Cosmos) | [Link](./src/CoreEx.Cosmos)
`CoreEx.Database` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Database.svg)](https://badge.fury.io/nu/CoreEx.Database) | [Link](./src/CoreEx.Database)
`CoreEx.Database.SqlServer` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Database.SqlServer.svg)](https://badge.fury.io/nu/CoreEx.Database.SqlServer) | [Link](./src/CoreEx.Database.SqlServer)
`CoreEx.Database.MySql` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Database.MySql.svg)](https://badge.fury.io/nu/CoreEx.Database.MySql) | [Link](./src/CoreEx.Database.MySql)
`CoreEx.Database.Postgres` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Database.Postgres.svg)](https://badge.fury.io/nu/CoreEx.Database.Postgres) | [Link](./src/CoreEx.Database.Postgres)
`CoreEx.Database.SqlServer` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Database.SqlServer.svg)](https://badge.fury.io/nu/CoreEx.Database.SqlServer) | [Link](./src/CoreEx.Database.SqlServer)
`CoreEx.EntityFrameworkCore` | [![NuGet version](https://badge.fury.io/nu/CoreEx.EntityFrameworkCore.svg)](https://badge.fury.io/nu/CoreEx.EntityFrameworkCore) | [Link](./src/CoreEx.EntityFrameworkCore)
`CoreEx.FluentValidation` | [![NuGet version](https://badge.fury.io/nu/CoreEx.FluentValidation.svg)](https://badge.fury.io/nu/CoreEx.FluentValidation) | [Link](./src/CoreEx.FluentValidation)
`CoreEx.Newtonsoft` | [![NuGet version](https://badge.fury.io/nu/CoreEx.Newtonsoft.svg)](https://badge.fury.io/nu/CoreEx.Newtonsoft) | [Link](./src/CoreEx.Newtonsoft)
Expand Down Expand Up @@ -59,9 +60,9 @@ These other _Avanade_ repositories leverage _CoreEx_:

Repo | Description
-|-
[Beef](https://github.com/Avanade/beef) | Code-generation capabilities to support the industrialization of API development leveraging `CoreEx` as the primary runtime framework (_Beef_ version `v5+`).
[DbEx](https://github.com/Avanade/dbex) | Provides database extensions for DbUp-inspired database migrations.
[NTangle](https://github.com/Avanade/ntangle) | Change Data Capture (CDC) code generation tool and runtime.
[*Beef*](https://github.com/Avanade/beef) | Code-generation capabilities to support the industrialization of API development leveraging `CoreEx` as the primary runtime framework (_Beef_ version `v5+`).
[*DbEx*](https://github.com/Avanade/dbex) | Provides database extensions for DbUp-inspired database migrations.
[*NTangle*](https://github.com/Avanade/ntangle) | Change Data Capture (CDC) code generation tool and runtime.

<br/>

Expand Down
1 change: 1 addition & 0 deletions nuget-publish.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ param(
"src\CoreEx.Database",
"src\CoreEx.Database.SqlServer",
"src\CoreEx.Database.MySql",
"src\CoreEx.Database.Postgres",
"src\CoreEx.EntityFrameworkCore",
"src\CoreEx.Cosmos",
"src\CoreEx.OData",
Expand Down
4 changes: 2 additions & 2 deletions samples/My.Hr/My.Hr.Api/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using Azure.Monitor.OpenTelemetry.AspNetCore;
using OpenTelemetry.Instrumentation.AspNetCore;
using OpenTelemetry.Trace;
using Az = Azure.Messaging.ServiceBus;

namespace My.Hr.Api;

Expand All @@ -28,9 +28,9 @@ public void ConfigureServices(IServiceCollection services)
.AddEventDataSerializer()
.AddEventDataFormatter()
.AddEventPublisher()
.AddSingleton(sp => new Az.ServiceBusClient(sp.GetRequiredService<HrSettings>().ServiceBusConnection__fullyQualifiedNamespace))
.AddAzureServiceBusSender()
.AddAzureServiceBusPurger()
.AddAzureServiceBusClient(connectionName: nameof(HrSettings.ServiceBusConnection), configure: (o, sp) => o.RetryOptions.MaxRetries = 3)
.AddJsonMergePatch()
.AddWebApi((_, c) => c.UnhandledExceptionAsync = (ex, _, _) => Task.FromResult(ex is DbUpdateConcurrencyException efex ? WebApiBase.CreateActionResultFromExtendedException(new ConcurrencyException()) : null))
.AddReferenceDataContentWebApi()
Expand Down
7 changes: 3 additions & 4 deletions samples/My.Hr/My.Hr.Functions/Startup.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,15 @@
using CoreEx.Database;
using CoreEx.DataBase.HealthChecks;
using CoreEx.HealthChecks;
using CoreEx.RefData;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Diagnostics.HealthChecks;
using My.Hr.Business;
using My.Hr.Business.Data;
using My.Hr.Business.External;
using My.Hr.Business.Services;
using Az = Azure.Messaging.ServiceBus;

[assembly: FunctionsStartup(typeof(My.Hr.Functions.Startup))]

Expand All @@ -40,12 +39,12 @@ public override void Configure(IFunctionsHostBuilder builder)
.AddEventDataSerializer()
.AddEventDataFormatter()
.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))
.AddJsonMergePatch()
.AddWebApiPublisher()
.AddAzureServiceBusSubscriber()
.AddAzureServiceBusClient(connectionName: nameof(HrSettings.ServiceBusConnection));
.AddAzureServiceBusSubscriber();

// Register the health checks.
builder.Services
Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.UnitTest/appsettings.unittest.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"DOTNET_ENVIRONMENT": "Development",
"VerificationResultsQueueName": "verificationResults",
"VerificationQueueName": "pendingVerifications",
"ServiceBusConnection__fullyQualifiedNamespace": "topsecret",
"ServiceBusConnection__fullyQualifiedNamespace": "Endpoint=sb://top-secret.servicebus.windows.net/;SharedAccessKeyName=top-secret;SharedAccessKey=top-encrypted-secret;",
"AgifyApiEndpointUri": "https://api.agify.mock.io",
"NationalizeApiClientApiEndpointUri": "https://api.nationalize.mock.io",
"GenderizeApiClientApiEndpointUri": "https://api.genderize.mock.io",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public static Task<ReferenceDataMultiDictionary> GetNamedAsync(this ReferenceDat
/// <summary>
/// Perform a further split of the string values.
/// </summary>
private static IEnumerable<string> SplitStringValues(IEnumerable<string> values)
private static List<string> SplitStringValues(IEnumerable<string> values)
{
var list = new List<string>();
foreach (var value in values)
Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx.AspNetCore/HealthChecks/HealthService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public async Task<IActionResult> RunAsync()
/// <summary>
/// Builds the health report response.
/// </summary>
private IActionResult BuildResponse(HealthReport healthReport)
private ContentResult BuildResponse(HealthReport healthReport)
{
var code = healthReport.Status == HealthStatus.Healthy ? HttpStatusCode.OK : HttpStatusCode.ServiceUnavailable;

Expand Down
5 changes: 2 additions & 3 deletions src/CoreEx.AspNetCore/WebApis/ValueContentResult.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
using CoreEx.AspNetCore.Http;
using CoreEx.Entities;
using CoreEx.Json;
using CoreEx.Results;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;
Expand Down Expand Up @@ -159,12 +158,12 @@ public static bool TryCreateValueContentResult<T>(T value, HttpStatusCode status
bool hasETag = TryGetETag(val, out var etag);

Action<IJsonPreFilterInspector>? inspector;
if (requestOptions.IncludeFields != null && requestOptions.IncludeFields.Any())
if (requestOptions.IncludeFields != null && requestOptions.IncludeFields.Length > 0)
{
inspector = hasETag ? null : fi => etag = GenerateETag(requestOptions, val, fi.ToJsonString(), jsonSerializer);
jsonSerializer.TryApplyFilter(val, requestOptions.IncludeFields, out json, JsonPropertyFilter.Include, preFilterInspector: inspector);
}
else if (requestOptions.ExcludeFields != null && requestOptions.ExcludeFields.Any())
else if (requestOptions.ExcludeFields != null && requestOptions.ExcludeFields.Length > 0)
{
inspector = hasETag ? null : fi => etag = GenerateETag(requestOptions, val, fi.ToJsonString(), jsonSerializer);
jsonSerializer.TryApplyFilter(val, requestOptions.ExcludeFields, out json, JsonPropertyFilter.Exclude, preFilterInspector: inspector);
Expand Down
6 changes: 3 additions & 3 deletions src/CoreEx.AspNetCore/WebApis/WebApi.cs
Original file line number Diff line number Diff line change
Expand Up @@ -695,7 +695,7 @@ private async Task<IActionResult> PutInternalAsync<TValue>(HttpRequest request,

// Get the current value before we perform the update; also performing a concurrency match.
var cvalue = await get(wap, ct).ConfigureAwait(false);
var ex = cvalue == null ? new NotFoundException() : ConcurrencyETagMatching(wap, cvalue, wapv!.Value, simulatedConcurrency);
var ex = cvalue == null ? (Exception)new NotFoundException() : ConcurrencyETagMatching(wap, cvalue, wapv!.Value, simulatedConcurrency);
if (ex is not null)
return await CreateActionResultFromExceptionAsync(this, request.HttpContext, ex, Settings, Logger, OnUnhandledExceptionAsync, cancellationToken).ConfigureAwait(false);

Expand All @@ -708,7 +708,7 @@ private async Task<IActionResult> PutInternalAsync<TValue>(HttpRequest request,
/// <summary>
/// Where etags are supported or automatic concurrency then we need to make sure one was provided up-front and match.
/// </summary>
private Exception? ConcurrencyETagMatching<TValue>(WebApiParam wap, TValue getValue, TValue putValue, bool autoConcurrency)
private ConcurrencyException? ConcurrencyETagMatching<TValue>(WebApiParam wap, TValue getValue, TValue putValue, bool autoConcurrency)
{
var et = putValue as IETag;
if (et != null || autoConcurrency)
Expand Down Expand Up @@ -844,7 +844,7 @@ public async Task<IActionResult> PatchAsync<TValue>(HttpRequest request, Func<We
{
// Get the current value and perform a concurrency match before we perform the merge.
var value = await get(wap, ct2).ConfigureAwait(false);
var ex = value is null ? new NotFoundException() : ConcurrencyETagMatching(wap, value, jpv, simulatedConcurrency);
var ex = value is null ? (Exception)new NotFoundException() : ConcurrencyETagMatching(wap, value, jpv, simulatedConcurrency);
if (ex is not null)
throw ex;

Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx.AspNetCore/WebApis/WebApiBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,7 @@ public static IActionResult CreateActionResultFromExtendedException(IExtendedExc
/// <summary>
/// Creates an <see cref="IActionResult"/> from an unexpected <paramref name="exception"/>.
/// </summary>
private static IActionResult CreateActionResultForUnexpectedResult(Exception exception, bool includeExceptionInResult) => includeExceptionInResult
private static ContentResult CreateActionResultForUnexpectedResult(Exception exception, bool includeExceptionInResult) => includeExceptionInResult
? new ContentResult { StatusCode = (int)HttpStatusCode.InternalServerError, ContentType = MediaTypeNames.Text.Plain, Content = $"An unexpected internal server error has occurred. CorrelationId={ExecutionContext.Current.CorrelationId} Exception={exception}" }
: new ContentResult { StatusCode = (int)HttpStatusCode.InternalServerError, ContentType = MediaTypeNames.Text.Plain, Content = $"An unexpected internal server error has occurred. CorrelationId={ExecutionContext.Current.CorrelationId}" };
}
Expand Down
11 changes: 3 additions & 8 deletions src/CoreEx.AutoMapper/AutoMapperConverterWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,18 @@ namespace CoreEx.Mapping.Converters
/// <typeparam name="TDestination">The destination <see cref="Type"/>.</typeparam>
/// <typeparam name="TConverter">The <see cref="IConverter{TSource, TDestination}"/> <see cref="Type"/>.</typeparam>
/// <typeparam name="TSelf">The declaring <see cref="Type"/> itself to enable <see cref="Default"/>.</typeparam>
public abstract class AutoMapperConverterWrapper<TSource, TDestination, TConverter, TSelf>
/// <param name="create">The optional function to create the <typeparamref name="TConverter"/> instance.</param>
public abstract class AutoMapperConverterWrapper<TSource, TDestination, TConverter, TSelf>(Func<TConverter>? create = null)
where TConverter : IConverter<TSource, TDestination>, new()
where TSelf : AutoMapperConverterWrapper<TSource, TDestination, TConverter, TSelf>, new()
{
private readonly Func<TConverter> _create;
private readonly Func<TConverter> _create = create ?? (() => new TConverter());

/// <summary>
/// Gets or sets the default (singleton) instance.
/// </summary>
public static TSelf Default { get; set; } = new();

/// <summary>
/// Initializes a new instance of the <see cref="AutoMapperConverterWrapper{TSource, TDestination, TConverter, TSelf}"/> class.
/// </summary>
/// <param name="create">The optional function to create the <typeparamref name="TConverter"/> instance.</param>
public AutoMapperConverterWrapper(Func<TConverter>? create = null) => _create = create ?? (() => new TConverter());

/// <summary>
/// Gets the source to destination <see cref="AutoMapper.IValueConverter{TSource, TDestination}"/>.
/// </summary>
Expand Down
Loading

0 comments on commit 7e45c7e

Please sign in to comment.