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.30.1 #133

Merged
merged 1 commit into from
Dec 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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

Represents the **NuGet** versions.

## v3.30.1
- *Fixed:* Added support for `SettingsBase.DateTimeTransform`, `StringTransform`, `StringTrim` and `StringCase` to allow specification via configuration.
- *Fixed:* Added support for `CoreEx:` hierarchy (optional) for all _CoreEx_ settings to enable a more structured and explicit configuration.

## v3.30.0
- *Enhancement:* Integrated `UnitTestEx` version `5.0.0` to enable the latest capabilities and improvements.
- `CoreEx.UnitTesting.NUnit` given changes is no longer required and has been deprecated, the `UnitTestEx.NUnit` (or other) must be explicitly referenced as per testing framework being used.
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.30.0</Version>
<Version>3.30.1</Version>
<LangVersion>preview</LangVersion>
<Authors>Avanade</Authors>
<Company>Avanade</Company>
Expand Down
18 changes: 10 additions & 8 deletions samples/My.Hr/My.Hr.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@
"Microsoft.AspNetCore": "Warning"
}
},
"PagingDefaultTake": 25,
"PagingMaxTake": 500,
"RefDataCache": {
"AbsoluteExpirationRelativeToNow": "01:45:00",
"SlidingExpiration": "00:15:00",
"Gender": {
"AbsoluteExpirationRelativeToNow": "03:00:00",
"SlidingExpiration": "00:45:00"
"CoreEx": {
"PagingDefaultTake": 25,
"PagingMaxTake": 500,
"RefDataCache": {
"AbsoluteExpirationRelativeToNow": "01:45:00",
"SlidingExpiration": "00:15:00",
"Gender": {
"AbsoluteExpirationRelativeToNow": "03:00:00",
"SlidingExpiration": "00:45:00"
}
}
},
"ServiceBusConnection": {
Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="DbEx.SqlServer" Version="2.6.1" />
<PackageReference Include="DbEx.SqlServer" Version="2.8.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
</ItemGroup>

Expand Down
3 changes: 3 additions & 0 deletions samples/My.Hr/My.Hr.UnitTest/EmployeeFunctionTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ public void A130_Get_IncludeFields()
using var test = FunctionTester.Create<Startup>();

var e = test.HttpTrigger<EmployeeFunction>()
.WithRouteCheck(UnitTestEx.Azure.Functions.RouteCheckOption.PathAndQueryStartsWith)
.Run(f => f.GetAsync(test.CreateHttpRequest(HttpMethod.Get, $"api/employees/{1.ToGuid()}", new CoreEx.Http.HttpRequestOptions().Include("FirstName", "LastName")), 1.ToGuid()))
.AssertOK()
.AssertJson("{\"firstName\":\"Wendy\",\"lastName\":\"Jones\"}");
Expand Down Expand Up @@ -114,6 +115,7 @@ public void B110_GetAll_Paging()
using var test = FunctionTester.Create<Startup>();

var v = test.HttpTrigger<EmployeeFunction>()
.WithRouteCheck(UnitTestEx.Azure.Functions.RouteCheckOption.PathAndQueryStartsWith)
.Run(f => f.GetAllAsync(test.CreateHttpRequest(HttpMethod.Get, "api/employees", CoreEx.Http.HttpRequestOptions.Create(PagingArgs.CreateSkipAndTake(1, 2, true)))))
.AssertOK()
.GetValue<EmployeeCollectionResult>();
Expand All @@ -134,6 +136,7 @@ public void B120_GetAll_PagingAndIncludeFields()
using var test = FunctionTester.Create<Startup>();

var v = test.HttpTrigger<EmployeeFunction>()
.WithRouteCheck(UnitTestEx.Azure.Functions.RouteCheckOption.PathAndQueryStartsWith)
.Run(f => f.GetAllAsync(test.CreateHttpRequest(HttpMethod.Get, "api/employees", CoreEx.Http.HttpRequestOptions.Create(PagingArgs.CreateSkipAndTake(1, 2, false)).Include("lastname"))))
.AssertOK()
.AssertJson("[ { \"lastName\": \"Jones\" }, { \"lastName\": \"Smith\" } ]")
Expand Down
2 changes: 1 addition & 1 deletion samples/My.Hr/My.Hr.UnitTest/My.Hr.UnitTest.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="UnitTestEx.NUnit" Version="5.0.0" />
<PackageReference Include="UnitTestEx.NUnit" Version="5.1.0" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,10 @@ public ServiceBusOrchestratedSubscriber(EventSubscriberOrchestrator orchestrator
{
Orchestrator = orchestrator.ThrowIfNull(nameof(orchestrator));
ServiceBusSubscriberInvoker = serviceBusSubscriberInvoker ?? (ServiceBusSubscriber._invoker ??= new ServiceBusSubscriberInvoker());
AbandonOnTransient = settings.GetValue($"{GetType().Name}__{nameof(AbandonOnTransient)}", false);
MaxDeliveryCount = settings.GetValue<int?>($"{GetType().Name}__{nameof(MaxDeliveryCount)}");
RetryDelay = settings.GetValue<TimeSpan?>($"{GetType().Name}__{nameof(RetryDelay)}");
MaxRetryDelay = settings.GetValue<TimeSpan?>($"{GetType().Name}__{nameof(MaxRetryDelay)}");
AbandonOnTransient = settings.GetCoreExValue($"{GetType().Name}:{nameof(AbandonOnTransient)}", false);
MaxDeliveryCount = settings.GetCoreExValue<int?>($"{GetType().Name}:{nameof(MaxDeliveryCount)}");
RetryDelay = settings.GetCoreExValue<TimeSpan?>($"{GetType().Name}:{nameof(RetryDelay)}");
MaxRetryDelay = settings.GetCoreExValue<TimeSpan?>($"{GetType().Name}:{nameof(MaxRetryDelay)}");
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions src/CoreEx.Azure/ServiceBus/ServiceBusPurger.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ private async Task PurgeAsync(string queueOrTopicName, string? subscriptionName,
queueOrTopicName.ThrowIfNullOrEmpty(nameof(queueOrTopicName));

// Get queue name and subscription name by checking settings override.
var qn = Settings.GetValue($"Publisher_ServiceBusQueueName_{queueOrTopicName}", defaultValue: queueOrTopicName);
var sn = string.IsNullOrEmpty(subscriptionName) ? null : Settings.GetValue($"Publisher_ServiceBusSubscriptionName_{subscriptionName}", defaultValue: subscriptionName);
var qn = Settings.GetCoreExValue($"Publisher_ServiceBusQueueName_{queueOrTopicName}", defaultValue: queueOrTopicName);
var sn = string.IsNullOrEmpty(subscriptionName) ? null : Settings.GetCoreExValue($"Publisher_ServiceBusSubscriptionName_{subscriptionName}", defaultValue: subscriptionName);

// Receive from Dead letter
var o = new ServiceBusReceiverOptions { SubQueue = subQueue, PrefetchCount = 500, ReceiveMode = ServiceBusReceiveMode.ReceiveAndDelete };
Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx.Azure/ServiceBus/ServiceBusSender.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ public Task SendAsync(IEnumerable<EventSendData> events, CancellationToken cance
{
var n = qitem.Key == _unspecifiedQueueOrTopicName ? null : qitem.Key;
var key = $"{GetType().Name}_QueueOrTopicName{(n is null ? "" : $"_{n}")}";
var qn = Settings.GetValue($"{GetType().Name}:QueueOrTopicName{(n is null ? "" : $"_{n}")}", defaultValue: n) ?? throw new EventSendException(PrependStats($"'{key}' configuration setting must have a non-null value.", totalCount, unsentEvents.Count), unsentEvents);
var qn = Settings.GetCoreExValue($"{GetType().Name}:QueueOrTopicName{(n is null ? "" : $"_{n}")}", defaultValue: n) ?? throw new EventSendException(PrependStats($"'{key}' configuration setting must have a non-null value.", totalCount, unsentEvents.Count), unsentEvents);
var queue = qitem.Value;
var sentIds = new List<string>();

Expand Down
8 changes: 4 additions & 4 deletions src/CoreEx.Azure/ServiceBus/ServiceBusSubscriber.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,10 +51,10 @@ public ServiceBusSubscriber(ExecutionContext executionContext, SettingsBase sett
: base(eventDataConverter ?? new ServiceBusReceivedMessageEventDataConverter(eventSerializer ?? new CoreEx.Text.Json.EventDataSerializer()), executionContext, settings, logger, eventSubscriberInvoker)
{
ServiceBusSubscriberInvoker = serviceBusSubscriberInvoker ?? (_invoker ??= new ServiceBusSubscriberInvoker());
AbandonOnTransient = settings.GetValue($"{GetType().Name}__{nameof(AbandonOnTransient)}", false);
MaxDeliveryCount = settings.GetValue<int?>($"{GetType().Name}__{nameof(MaxDeliveryCount)}");
RetryDelay = settings.GetValue<TimeSpan?>($"{GetType().Name}__{nameof(RetryDelay)}");
MaxRetryDelay = settings.GetValue<TimeSpan?>($"{GetType().Name}__{nameof(MaxRetryDelay)}");
AbandonOnTransient = settings.GetCoreExValue($"{GetType().Name}:{nameof(AbandonOnTransient)}", false);
MaxDeliveryCount = settings.GetCoreExValue<int?>($"{GetType().Name}:{nameof(MaxDeliveryCount)}");
RetryDelay = settings.GetCoreExValue<TimeSpan?>($"{GetType().Name}:{nameof(RetryDelay)}");
MaxRetryDelay = settings.GetCoreExValue<TimeSpan?>($"{GetType().Name}:{nameof(MaxRetryDelay)}");
}

/// <summary>
Expand Down
14 changes: 3 additions & 11 deletions src/CoreEx.Azure/Storage/TableWorkStatePersistence.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
using Azure;
using Azure.Data.Tables;
using CoreEx.Hosting.Work;
using CoreEx.Json;
using System;
using System.IO;
using System.Linq;
Expand All @@ -18,11 +17,8 @@ namespace CoreEx.Azure.Storage
/// <remarks>The maximum <see cref="BinaryData"/> size currently supported is 960,000 bytes.</remarks>
public class TableWorkStatePersistence : IWorkStatePersistence
{
private static readonly string[] _columns = [nameof(WorkState.TypeName), nameof(WorkState.CorrelationId), nameof(WorkState.Status), nameof(WorkState.Created), nameof(WorkState.Expiry), nameof(WorkState.Started), nameof(WorkState.Indeterminate), nameof(WorkState.Finished), nameof(WorkState.Reason)];

private readonly TableClient _workStateTableClient;
private readonly TableClient _workDataTableClient;
private readonly IJsonSerializer _jsonSerializer;
private readonly SemaphoreSlim _semaphore = new(1, 1);
private volatile bool _firstTime = true;

Expand All @@ -32,17 +28,15 @@ public class TableWorkStatePersistence : IWorkStatePersistence
/// <param name="tableServiceClient">The <see cref="TableServiceClient"/>.</param>
/// <param name="workStateTableName">The work state table name.</param>
/// <param name="workDataTableName">The work data table name.</param>
/// <param name="jsonSerializer">The <see cref="IJsonSerializer"/>. Defaults to <see cref="JsonSerializer.Default"/>.</param>
public TableWorkStatePersistence(TableServiceClient tableServiceClient, string workStateTableName = "workstate", string workDataTableName = "workdata", IJsonSerializer? jsonSerializer = null)
: this(tableServiceClient.ThrowIfNull(nameof(tableServiceClient)).GetTableClient(workStateTableName), tableServiceClient.GetTableClient(workDataTableName), jsonSerializer) { }
public TableWorkStatePersistence(TableServiceClient tableServiceClient, string workStateTableName = "workstate", string workDataTableName = "workdata")
: this(tableServiceClient.ThrowIfNull(nameof(tableServiceClient)).GetTableClient(workStateTableName), tableServiceClient.GetTableClient(workDataTableName)) { }

/// <summary>
/// Initializes a new instance of the <see cref="TableWorkStatePersistence"/> class.
/// </summary>
/// <param name="workStateTableClient">The work state <see cref="TableClient"/>.</param>
/// <param name="workDataTableClient">The work data <see cref="TableClient"/>.</param>
/// <param name="jsonSerializer">The <see cref="IJsonSerializer"/>. Defaults to <see cref="JsonSerializer.Default"/>.</param>
public TableWorkStatePersistence(TableClient workStateTableClient, TableClient workDataTableClient, IJsonSerializer? jsonSerializer = null)
public TableWorkStatePersistence(TableClient workStateTableClient, TableClient workDataTableClient)
{
if (workStateTableClient.ThrowIfNull(nameof(workStateTableClient)).Name == workDataTableClient.ThrowIfNull(nameof(workDataTableClient)).Name)
throw new ArgumentException("The work state and data table names must be different.", nameof(workDataTableClient));
Expand All @@ -52,8 +46,6 @@ public TableWorkStatePersistence(TableClient workStateTableClient, TableClient w

_workDataTableClient.CreateIfNotExists();
_workStateTableClient.CreateIfNotExists();

_jsonSerializer = jsonSerializer ?? JsonSerializer.Default;
}

private class WorkStateEntity() : WorkState, ITableEntity
Expand Down
2 changes: 1 addition & 1 deletion src/CoreEx.Data/CoreEx.Data.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
<Import Project="..\..\Common.targets" />

<ItemGroup>
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.4.9" />
<PackageReference Include="System.Linq.Dynamic.Core" Version="1.5.0" />
</ItemGroup>

<ItemGroup>
Expand Down
4 changes: 4 additions & 0 deletions src/CoreEx.Data/Querying/QueryFilterFieldConfigBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,11 @@ public virtual StringBuilder AppendToString(StringBuilder stringBuilder)
protected StringBuilder AppendOperatorsToString(StringBuilder stringBuilder)
{
var first = true;
#if NET6_0_OR_GREATER
foreach (var e in Enum.GetValues<QueryFilterOperator>())
#else
foreach (var e in Enum.GetValues(typeof(QueryFilterOperator)))
#endif
{
if (Operators.HasFlag((QueryFilterOperator)e))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public static class DatabaseServiceCollectionExtensions
/// <remarks>To turn off the execution of the <see cref="EventOutboxHostedService"/>(s) at runtime set the '<c>EventOutboxHostedService:Enabled</c>' configuration setting to <c>false</c>.</remarks>
public static IServiceCollection AddSqlServerEventOutboxHostedService(this IServiceCollection services, Func<IServiceProvider, EventOutboxDequeueBase> eventOutboxDequeueFactory, string? partitionKey = null, string? destination = null, bool healthCheck = true)
{
var exe = services.BuildServiceProvider().GetRequiredService<SettingsBase>().GetValue<bool?>("EventOutboxHostedService__Enabled");
var exe = services.BuildServiceProvider().GetRequiredService<SettingsBase>().GetCoreExValue<bool?>("EventOutboxHostedService:Enabled");
if (!exe.HasValue || exe.Value)
{
// Add the health check.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ public EventOutboxHostedService(IServiceProvider serviceProvider, ILogger<EventO
/// <remarks>Will default to <see cref="SettingsBase"/> configuration, a) <see cref="TimerHostedServiceBase.ServiceName"/> : <see cref="IntervalName"/>, then b) <see cref="IntervalName"/>, where specified; otherwise, <see cref="DefaultInterval"/>.</remarks>
public override TimeSpan Interval
{
get => _interval ?? Settings.GetValue<TimeSpan?>($"{ServiceName}:{IntervalName}".Replace(".", "_")) ?? Settings.GetValue<TimeSpan?>(IntervalName.Replace(".", "_")) ?? DefaultInterval;
get => _interval ?? Settings.GetCoreExValue<TimeSpan?>($"{ServiceName}:{IntervalName}".Replace(".", "_")) ?? Settings.GetCoreExValue<TimeSpan?>(IntervalName.Replace(".", "_")) ?? DefaultInterval;
set => _interval = value;
}

Expand All @@ -120,7 +120,7 @@ public override TimeSpan Interval
/// <remarks>Will default to <see cref="SettingsBase"/> configuration, a) <see cref="TimerHostedServiceBase.ServiceName"/> : <see cref="MaxDequeueSizeName"/>, then b) <see cref="MaxDequeueSizeName"/>, where specified; otherwise, 10.</remarks>
public int MaxDequeueSize
{
get => _maxDequeueSize ?? Settings.GetValue<int?>($"{ServiceName}:{MaxDequeueSizeName}".Replace(".", "_")) ?? Settings.GetValue<int?>(MaxDequeueSizeName.Replace(".", "_")) ?? 10;
get => _maxDequeueSize ?? Settings.GetCoreExValue<int?>($"{ServiceName}:{MaxDequeueSizeName}".Replace(".", "_")) ?? Settings.GetCoreExValue<int?>(MaxDequeueSizeName.Replace(".", "_")) ?? 10;
set => _maxDequeueSize = value;
}

Expand Down
4 changes: 2 additions & 2 deletions src/CoreEx.Database.SqlServer/Outbox/EventOutboxService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ public class EventOutboxService(IServiceProvider serviceProvider, ILogger<EventO
/// <remarks>Will default to <see cref="SettingsBase"/> configuration, a) <see cref="ServiceBase.ServiceName"/> : <see cref="ServiceBase.MaxIterationsName"/>, then b) <see cref="ServiceBase.MaxIterationsName"/>, where specified; otherwise, <see cref="ServiceBase.DefaultMaxIterations"/>.</remarks>
public override int MaxIterations
{
get => _maxIterations ?? Settings.GetValue<int?>($"{ServiceName}:{MaxIterationsName}".Replace(".", "_")) ?? Settings.GetValue<int?>(MaxIterationsName.Replace(".", "_")) ?? DefaultMaxIterations;
get => _maxIterations ?? Settings.GetCoreExValue<int?>($"{ServiceName}:{MaxIterationsName}".Replace(".", "_")) ?? Settings.GetCoreExValue<int?>(MaxIterationsName.Replace(".", "_")) ?? DefaultMaxIterations;
set => _maxIterations = value;
}

Expand All @@ -59,7 +59,7 @@ public override int MaxIterations
/// <remarks>Will default to <see cref="SettingsBase"/> configuration, a) <see cref="ServiceBase.ServiceName"/> : <see cref="MaxDequeueSizeName"/>, then b) <see cref="MaxDequeueSizeName"/>, where specified; otherwise, 10.</remarks>
public int MaxDequeueSize
{
get => _maxDequeueSize ?? Settings.GetValue<int?>($"{ServiceName}:{MaxDequeueSizeName}".Replace(".", "_")) ?? Settings.GetValue<int?>(MaxDequeueSizeName.Replace(".", "_")) ?? 10;
get => _maxDequeueSize ?? Settings.GetCoreExValue<int?>($"{ServiceName}:{MaxDequeueSizeName}".Replace(".", "_")) ?? Settings.GetCoreExValue<int?>(MaxDequeueSizeName.Replace(".", "_")) ?? 10;
set => _maxDequeueSize = value;
}

Expand Down
17 changes: 8 additions & 9 deletions src/CoreEx.Database/Mapping/DatabaseMapperT.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,19 +122,18 @@ public PropertyColumnMapper<TSource, TSourceProperty> Property<TSourceProperty>(
/// <summary>
/// Validates and adds a new IPropertyColumnMapper.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Usage", "CA2208:Instantiate argument exceptions correctly", Justification = "They are the arguments from the calling method.")]
private void AddMapping(IPropertyColumnMapper pcm)
private void AddMapping<TSourceProperty>(PropertyColumnMapper<TSource, TSourceProperty> propertyColumnMapper)
{
if (_mappings.Any(x => x.PropertyName == pcm.PropertyName))
throw new ArgumentException($"Source property '{pcm.PropertyName}' must not be specified more than once.", "propertyExpression");
if (_mappings.Any(x => x.PropertyName == propertyColumnMapper.PropertyName))
throw new ArgumentException($"Source property '{propertyColumnMapper.PropertyName}' must not be specified more than once.", nameof(propertyColumnMapper));

if (_mappings.Any(x => x.ColumnName == pcm.ColumnName))
throw new ArgumentException($"Column '{pcm.ColumnName}' must not be specified more than once.", "columnName");
if (_mappings.Any(x => x.ColumnName == propertyColumnMapper.ColumnName))
throw new ArgumentException($"Column '{propertyColumnMapper.ColumnName}' must not be specified more than once.", nameof(propertyColumnMapper));

if (_mappings.Any(x => x.ParameterName == pcm.ParameterName))
throw new ArgumentException($"Parameter '{pcm.ParameterName}' must not be specified more than once.", "parameterName");
if (_mappings.Any(x => x.ParameterName == propertyColumnMapper.ParameterName))
throw new ArgumentException($"Parameter '{propertyColumnMapper.ParameterName}' must not be specified more than once.", nameof(propertyColumnMapper));

_mappings.Add(pcm);
_mappings.Add(propertyColumnMapper);
}

/// <summary>
Expand Down
Loading
Loading