diff --git a/CHANGELOG.md b/CHANGELOG.md index 9fc2adbe..2cc0a6ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ Represents the **NuGet** versions. +## v3.14.1 +- *Fixed*: The `Result.ValidatesAsync` extension method signature has had the value nullability corrected to enable fluent-style method-chaining. +- *Fixed*: The fully qualified type and property name is now correctly used as the `LText.KeyAndOrText` when creating within the `PropertyExpression` to enable a qualified _key_ that can be used by the `ITextProvider` to substitute the text at runtime; the existing text fallback behavior remains such that an appropriate text is used. The `PropertyExpression.CreatePropertyLTextKey` function can be overridden to change this behavior. + ## v3.14.0 - *Enhancement*: Planned feature obsoletion. The `TypedHttpClientBase` methods `WithRetry`, `WithTimeout`, `WithCustomRetryPolicy` and `WithMaxRetryDelay` are now marked as obsolete and will result in a compile-time warning. Related `TypedHttpClientOptions`, `HttpRequestLogger` and `SettingsBase` capabilities have also been obsoleted. - Why? Primarily based on Microsoft guidance around [`IHttpClientFactory`](https://learn.microsoft.com/en-us/dotnet/core/extensions/httpclient-factory) usage. Specifically advances in native HTTP [resilency](https://learn.microsoft.com/en-us/dotnet/core/resilience/http-resilience) support, and the [.NET 8 networking improvements](https://devblogs.microsoft.com/dotnet/dotnet-8-networking-improvements/). diff --git a/Common.targets b/Common.targets index 00646e62..5a343516 100644 --- a/Common.targets +++ b/Common.targets @@ -1,6 +1,6 @@  - 3.14.0 + 3.14.1 preview Avanade Avanade diff --git a/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj b/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj index 879c575e..0f4855b6 100644 --- a/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj +++ b/samples/My.Hr/My.Hr.Database/My.Hr.Database.csproj @@ -21,7 +21,8 @@ - + + diff --git a/samples/My.Hr/My.Hr.UnitTest/My.Hr.UnitTest.csproj b/samples/My.Hr/My.Hr.UnitTest/My.Hr.UnitTest.csproj index 9f191100..70c968c9 100644 --- a/samples/My.Hr/My.Hr.UnitTest/My.Hr.UnitTest.csproj +++ b/samples/My.Hr/My.Hr.UnitTest/My.Hr.UnitTest.csproj @@ -20,14 +20,14 @@ - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/CoreEx.Azure/CoreEx.Azure.csproj b/src/CoreEx.Azure/CoreEx.Azure.csproj index 7d1d1879..032feb95 100644 --- a/src/CoreEx.Azure/CoreEx.Azure.csproj +++ b/src/CoreEx.Azure/CoreEx.Azure.csproj @@ -14,13 +14,13 @@ - - + + - + - - + + diff --git a/src/CoreEx.Cosmos/CoreEx.Cosmos.csproj b/src/CoreEx.Cosmos/CoreEx.Cosmos.csproj index d8bb53f6..cfcb838d 100644 --- a/src/CoreEx.Cosmos/CoreEx.Cosmos.csproj +++ b/src/CoreEx.Cosmos/CoreEx.Cosmos.csproj @@ -12,8 +12,8 @@ - - + + diff --git a/src/CoreEx.Database.SqlServer/CoreEx.Database.SqlServer.csproj b/src/CoreEx.Database.SqlServer/CoreEx.Database.SqlServer.csproj index 44ee37fe..22285fdd 100644 --- a/src/CoreEx.Database.SqlServer/CoreEx.Database.SqlServer.csproj +++ b/src/CoreEx.Database.SqlServer/CoreEx.Database.SqlServer.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/CoreEx.EntityFrameworkCore/CoreEx.EntityFrameworkCore.csproj b/src/CoreEx.EntityFrameworkCore/CoreEx.EntityFrameworkCore.csproj index dd629e48..3d731205 100644 --- a/src/CoreEx.EntityFrameworkCore/CoreEx.EntityFrameworkCore.csproj +++ b/src/CoreEx.EntityFrameworkCore/CoreEx.EntityFrameworkCore.csproj @@ -13,15 +13,15 @@ - + - + - + diff --git a/src/CoreEx.UnitTesting.NUnit/CoreEx.UnitTesting.NUnit.csproj b/src/CoreEx.UnitTesting.NUnit/CoreEx.UnitTesting.NUnit.csproj index 381e7eba..99fc618f 100644 --- a/src/CoreEx.UnitTesting.NUnit/CoreEx.UnitTesting.NUnit.csproj +++ b/src/CoreEx.UnitTesting.NUnit/CoreEx.UnitTesting.NUnit.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/CoreEx.UnitTesting/CoreEx.UnitTesting.csproj b/src/CoreEx.UnitTesting/CoreEx.UnitTesting.csproj index 99a2e997..76321cae 100644 --- a/src/CoreEx.UnitTesting/CoreEx.UnitTesting.csproj +++ b/src/CoreEx.UnitTesting/CoreEx.UnitTesting.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/CoreEx.Validation/IEntityRule.cs b/src/CoreEx.Validation/IEntityRule.cs new file mode 100644 index 00000000..da107a70 --- /dev/null +++ b/src/CoreEx.Validation/IEntityRule.cs @@ -0,0 +1,22 @@ +// Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace CoreEx.Validation +{ + /// + /// Provides a validation rule for an entity. + /// + /// The entity . + public interface IEntityRule where TEntity : class + { + /// + /// Validates an entity given a . + /// + /// The + /// The . + Task ValidateAsync(ValidationContext context, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/CoreEx.Validation/IPropertyRule.cs b/src/CoreEx.Validation/IPropertyRule.cs index a3b08bbc..c58eff72 100644 --- a/src/CoreEx.Validation/IPropertyRule.cs +++ b/src/CoreEx.Validation/IPropertyRule.cs @@ -1,22 +1,48 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx -using System; +using CoreEx.Localization; using System.Threading; using System.Threading.Tasks; namespace CoreEx.Validation { /// - /// Provides a validation rule for a property. + /// Enables a validation rule for an entity property. /// - /// The entity . - public interface IPropertyRule where TEntity : class + public interface IPropertyRule { /// - /// Validates an entity given a . + /// Gets the property name. + /// + public string Name { get; } + + /// + /// Gets the JSON property name. + /// + public string JsonName { get; } + + /// + /// Gets or sets the friendly text name used in validation messages. + /// + public LText Text { get; set; } + + /// + /// Executes the validation for the property value. /// - /// The /// The . - Task ValidateAsync(ValidationContext context, CancellationToken cancellationToken); + /// A . + Task ValidateAsync(CancellationToken cancellationToken = default); + + /// + /// Executes the validation for the property value. + /// + /// Indicates whether to automatically throw a where . + /// >The . + /// A . + public async Task ValidateAsync(bool throwOnError, CancellationToken cancellationToken = default) + { + var ir = await ValidateAsync(cancellationToken).ConfigureAwait(false); + return throwOnError ? ir.ThrowOnError() : ir; + } } } \ No newline at end of file diff --git a/src/CoreEx.Validation/IPropertyRuleT2.cs b/src/CoreEx.Validation/IPropertyRuleT2.cs index 4ad775b7..0a9d9d82 100644 --- a/src/CoreEx.Validation/IPropertyRuleT2.cs +++ b/src/CoreEx.Validation/IPropertyRuleT2.cs @@ -1,12 +1,9 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx -using CoreEx.Localization; using CoreEx.Validation.Clauses; using CoreEx.Validation.Rules; using System; using System.Linq.Expressions; -using System.Threading; -using System.Threading.Tasks; namespace CoreEx.Validation { @@ -15,23 +12,8 @@ namespace CoreEx.Validation /// /// The entity . /// The property . - public interface IPropertyRule where TEntity : class + public interface IPropertyRule : IPropertyRule where TEntity : class { - /// - /// Gets the property name. - /// - public string Name { get; } - - /// - /// Gets the JSON property name. - /// - public string JsonName { get; } - - /// - /// Gets or sets the friendly text name used in validation messages. - /// - public LText Text { get; set; } - /// /// Adds a clause () to the rule. /// @@ -45,25 +27,6 @@ public interface IPropertyRule where TEntity : class /// The . IPropertyRule AddRule(IValueRule rule); - /// - /// Executes the validation for the property value. - /// - /// The . - /// A . - Task ValidateAsync(CancellationToken cancellationToken = default); - - /// - /// Executes the validation for the property value. - /// - /// Indicates whether to automatically throw a where . - /// >The . - /// A . - public async Task ValidateAsync(bool throwOnError, CancellationToken cancellationToken = default) - { - var ir = await ValidateAsync(cancellationToken).ConfigureAwait(false); - return throwOnError ? ir.ThrowOnError() : ir; - } - /// /// Adds a to this in that another specified property of the entity must have a non-default value (and not have a validation error) to continue. /// diff --git a/src/CoreEx.Validation/IncludeBaseRule.cs b/src/CoreEx.Validation/IncludeBaseRule.cs index 14ab5ebf..d8d632b5 100644 --- a/src/CoreEx.Validation/IncludeBaseRule.cs +++ b/src/CoreEx.Validation/IncludeBaseRule.cs @@ -11,7 +11,7 @@ namespace CoreEx.Validation /// /// The entity . /// The entity base . - public class IncludeBaseRule : ValidatorBase, IPropertyRule where TEntity : class where TInclude : class + public class IncludeBaseRule : ValidatorBase, IEntityRule where TEntity : class where TInclude : class { private readonly IValidatorEx _include; diff --git a/src/CoreEx.Validation/PropertyRule.cs b/src/CoreEx.Validation/PropertyRule.cs index 1be4e0ac..2127c7cf 100644 --- a/src/CoreEx.Validation/PropertyRule.cs +++ b/src/CoreEx.Validation/PropertyRule.cs @@ -15,7 +15,7 @@ namespace CoreEx.Validation /// /// The entity . /// The property . - public class PropertyRule : PropertyRuleBase, IPropertyRule, IValueRule where TEntity : class + public class PropertyRule : PropertyRuleBase, IEntityRule, IValueRule where TEntity : class { private readonly PropertyExpression _property; diff --git a/src/CoreEx.Validation/PropertyRuleBase.cs b/src/CoreEx.Validation/PropertyRuleBase.cs index d0cf876d..398cf743 100644 --- a/src/CoreEx.Validation/PropertyRuleBase.cs +++ b/src/CoreEx.Validation/PropertyRuleBase.cs @@ -108,6 +108,6 @@ protected async Task InvokeAsync(PropertyContext context, Ca public abstract Task> ValidateAsync(CancellationToken cancellationToken = default); /// - async Task IPropertyRule.ValidateAsync(CancellationToken cancellationToken) => await ValidateAsync(cancellationToken).ConfigureAwait(false); + async Task IPropertyRule.ValidateAsync(CancellationToken cancellationToken) => await ValidateAsync(cancellationToken).ConfigureAwait(false); } } \ No newline at end of file diff --git a/src/CoreEx.Validation/RuleSet.cs b/src/CoreEx.Validation/RuleSet.cs index 3621f730..8a160dc7 100644 --- a/src/CoreEx.Validation/RuleSet.cs +++ b/src/CoreEx.Validation/RuleSet.cs @@ -10,7 +10,7 @@ namespace CoreEx.Validation /// Represents a validation rule set for an entity, in that it groups one or more together for a specified condition. /// /// The entity . - public class RuleSet : ValidatorBase, IPropertyRule where TEntity : class + public class RuleSet : ValidatorBase, IEntityRule where TEntity : class { /// /// Initializes a new instance of the class to be invoked where the predicate is true. diff --git a/src/CoreEx.Validation/ValidationExtensions.cs b/src/CoreEx.Validation/ValidationExtensions.cs index 6c369830..3dada529 100644 --- a/src/CoreEx.Validation/ValidationExtensions.cs +++ b/src/CoreEx.Validation/ValidationExtensions.cs @@ -26,7 +26,7 @@ public static class ValidationExtensions #region Text /// - /// Updates the rule friendly name text used in validation messages (see . + /// Updates the rule friendly name text used in validation messages (see . /// /// The entity . /// The property . @@ -1212,7 +1212,7 @@ public static async Task> ValidateAsync(this TaskThe . /// The resulting . /// Validation only occurs where the is not null; otherwise, continues as expected. - public static async Task ValidatesAsync(this TResult result, T value, Func, T?>, IPropertyRule, T?>>? validator, string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult + public static async Task ValidatesAsync(this TResult result, T value, Func, T>, IPropertyRule>? validator, string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult #else /// /// Executes the for the specified where the is . @@ -1227,7 +1227,7 @@ public static async Task ValidatesAsync(this TResult result /// The . /// The resulting . /// Validation only occurs where the is not null; otherwise, continues as expected. - public static async Task ValidatesAsync(this TResult result, T value, Func, T?>, IPropertyRule, T?>>? validator, [CallerArgumentExpression(nameof(value))] string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult + public static async Task ValidatesAsync(this TResult result, T value, Func, T>, IPropertyRule>? validator, [CallerArgumentExpression(nameof(value))] string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult #endif { if (validator is null || result.IsFailure) @@ -1252,7 +1252,7 @@ public static async Task ValidatesAsync(this TResult result /// The . /// The resulting . /// Validation only occurs where the is not null; otherwise, continues as expected. - public static async Task ValidatesAsync(this Task result, T value, Func, T?>, IPropertyRule, T?>>? validator, string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult + public static async Task ValidatesAsync(this Task result, T value, Func, T>, IPropertyRule>? validator, string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult #else /// /// Executes the for the specified where the is . @@ -1267,7 +1267,7 @@ public static async Task ValidatesAsync(this Task /// The . /// The resulting . /// Validation only occurs where the is not null; otherwise, continues as expected. - public static async Task ValidatesAsync(this Task result, T value, Func, T?>, IPropertyRule, T?>>? validator, [CallerArgumentExpression(nameof(value))] string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult + public static async Task ValidatesAsync(this Task result, T value, Func, T>, IPropertyRule>? validator, [CallerArgumentExpression(nameof(value))] string? name = default, LText? text = default, CancellationToken cancellationToken = default) where TResult : IResult #endif { var r = await result.ConfigureAwait(false); diff --git a/src/CoreEx.Validation/ValidatorBase.cs b/src/CoreEx.Validation/ValidatorBase.cs index 7e8a35c3..31e3987f 100644 --- a/src/CoreEx.Validation/ValidatorBase.cs +++ b/src/CoreEx.Validation/ValidatorBase.cs @@ -17,7 +17,7 @@ public abstract class ValidatorBase : IValidatorEx where TEnti /// /// Gets the underlying rules collection. /// - internal protected List> Rules { get; } = []; + internal protected List> Rules { get; } = []; /// /// Gets the instance. diff --git a/src/CoreEx/Abstractions/Reflection/PropertyExpression.cs b/src/CoreEx/Abstractions/Reflection/PropertyExpression.cs index 3cbba158..69b00657 100644 --- a/src/CoreEx/Abstractions/Reflection/PropertyExpression.cs +++ b/src/CoreEx/Abstractions/Reflection/PropertyExpression.cs @@ -1,9 +1,13 @@ // Copyright (c) Avanade. Licensed under the MIT License. See https://github.com/Avanade/CoreEx using CoreEx.Json; +using CoreEx.Localization; +using CoreEx.Text; using Microsoft.Extensions.Caching.Memory; using System; +using System.ComponentModel.DataAnnotations; using System.Linq.Expressions; +using System.Reflection; namespace CoreEx.Abstractions.Reflection { @@ -19,6 +23,25 @@ public static partial class PropertyExpression /// internal static IMemoryCache Cache => ExecutionContext.GetService() ?? (_fallbackCache ??= new MemoryCache(new MemoryCacheOptions())); + /// + /// Gets or sets the absolute expiration . + /// + /// Defaults to 4 hours. + public static TimeSpan? AbsoluteExpirationTimespan { get; set; } = TimeSpan.FromHours(4); + + /// + /// Gets or sets the sliding expiration . + /// + /// Defaults to 30 minutes. + public static TimeSpan? SlidingExpirationTimespan { get; set; } = TimeSpan.FromMinutes(30); + + /// + /// Gets or sets the function to create the for the specified entity , and inferred text (being either the corresponding where specified + /// or the converted to ). + /// + /// Defaults the to the fully qualified name (being + '.' + ) and the to the inferred text. + public static Func CreatePropertyLText { get; set; } = (entityType, propertyInfo, text) => new LText($"{entityType.FullName}.{propertyInfo.Name}", text); + /// /// Validates, creates and compiles the property expression; whilst also determinig the property friendly . /// diff --git a/src/CoreEx/Abstractions/Reflection/PropertyExpressionT.cs b/src/CoreEx/Abstractions/Reflection/PropertyExpressionT.cs index a6b97050..8c4c62e7 100644 --- a/src/CoreEx/Abstractions/Reflection/PropertyExpressionT.cs +++ b/src/CoreEx/Abstractions/Reflection/PropertyExpressionT.cs @@ -17,23 +17,12 @@ namespace CoreEx.Abstractions.Reflection /// /// The entity . /// The property . - /// The internal reflection comes at a performance cost; as such the resulting is cached using an . The - /// and enable additional basic policy configuration for the cached items. + /// The internal reflection comes at a performance cost; as such the resulting is cached using an . The + /// and enable additional basic policy configuration for the cached items. public class PropertyExpression : IPropertyExpression { private readonly Func _getValue; private readonly Action? _setValue; - private string? _text; - - /// - /// Gets or sets the absolute expiration . Default to 4 hours. - /// - public static TimeSpan AbsoluteExpirationTimespan { get; set; } = TimeSpan.FromHours(4); - - /// - /// Gets or sets the sliding expiration . Default to 30 minutes. - /// - public static TimeSpan SlidingExpirationTimespan { get; set; } = TimeSpan.FromMinutes(30); /// /// Validates, creates and compiles the property expression; whilst also determinig the property friendly . @@ -52,8 +41,11 @@ internal static PropertyExpression CreateInternal(Expression // Check cache and reuse as this is a *really* expensive operation. Key contains: Entity type, property name, and json serializer (in case configuration is different). return cache.GetOrCreate((typeof(TEntity), me.Member.Name, jsonSerializer.GetType()), ce => { - ce.SetAbsoluteExpiration(AbsoluteExpirationTimespan); - ce.SetSlidingExpiration(SlidingExpirationTimespan); + if (PropertyExpression.AbsoluteExpirationTimespan.HasValue) + ce.SetAbsoluteExpiration(PropertyExpression.AbsoluteExpirationTimespan.Value); + + if (PropertyExpression.SlidingExpirationTimespan.HasValue) + ce.SetSlidingExpiration(PropertyExpression.SlidingExpirationTimespan.Value); if (me.Member.MemberType != MemberTypes.Property) throw new InvalidOperationException("Expression results in a Member that is not a Property."); @@ -103,7 +95,7 @@ private PropertyExpression(PropertyInfo pi, string name, string? jsonName, strin PropertyInfo = pi; Name = name; JsonName = jsonName; - _text = text; + Text = PropertyExpression.CreatePropertyLText(typeof(TEntity), pi, text ?? Name.ToSentenceCase()); IsJsonSerializable = isSerializable; IsClass = PropertyInfo.PropertyType.IsClass && PropertyInfo.PropertyType != typeof(string); _getValue = getValue; @@ -120,7 +112,7 @@ private PropertyExpression(PropertyInfo pi, string name, string? jsonName, strin public string? JsonName { get; } /// - public LText Text => _text ??= Name.ToSentenceCase()!; // Lazy generate the text to avoid logic execution if not needed. + public LText Text { get; } /// public bool IsJsonSerializable { get; } diff --git a/src/CoreEx/AuthenticationException.cs b/src/CoreEx/AuthenticationException.cs index edf0e319..88f97cfa 100644 --- a/src/CoreEx/AuthenticationException.cs +++ b/src/CoreEx/AuthenticationException.cs @@ -13,7 +13,7 @@ namespace CoreEx /// The defaults to: An authentication error occured; the credentials you provided are not valid. public class AuthenticationException : Exception, IExtendedException { - private const string _message = "An authorization error occurred; you are not permitted to perform this action."; + private const string _message = "An authentication error occurred; the credentials you provided are not valid."; /// /// Get or sets the value. diff --git a/src/CoreEx/CoreEx.csproj b/src/CoreEx/CoreEx.csproj index e3c2cc87..b0eb08b4 100644 --- a/src/CoreEx/CoreEx.csproj +++ b/src/CoreEx/CoreEx.csproj @@ -15,20 +15,20 @@ - + - + - + @@ -41,7 +41,7 @@ - + @@ -54,7 +54,7 @@ - + @@ -69,7 +69,7 @@ - + diff --git a/src/CoreEx/Localization/LText.cs b/src/CoreEx/Localization/LText.cs index 4d0d3b01..bac2c64c 100644 --- a/src/CoreEx/Localization/LText.cs +++ b/src/CoreEx/Localization/LText.cs @@ -7,8 +7,14 @@ namespace CoreEx.Localization /// /// Represents the localization text key/identifier to be used by the . /// - public struct LText + public struct LText : IEquatable { + /// + /// Gets the empty . + /// + /// The and are both null. + public static readonly LText Empty = new(); + /// /// Gets or sets the numeric () key/identifier format to convert to a standardized . /// @@ -52,16 +58,40 @@ public LText(long key, string? fallbackText = null) public string? KeyAndOrText { get; } /// - /// Gets or sets the optional fallback text to be used when the is not found by the (where not specified that becomes the fallback text). + /// Gets or sets the optional fallback text to be used when the is not found by the (where not specified the becomes the fallback text). /// public string? FallbackText { get; set; } + /// + /// Indicates whether the is empty; i.e. the and are both null. + /// + public readonly bool IsEmpty => KeyAndOrText is null && FallbackText is null; + /// /// Returns the as a (see ). /// /// The string value. public override readonly string ToString() => this!; + /// + public override readonly bool Equals(object? obj) => obj is LText r && Equals(r); + + /// + public readonly bool Equals(LText other) => KeyAndOrText == other.KeyAndOrText && FallbackText == other.FallbackText; + + /// + public override readonly int GetHashCode() => HashCode.Combine(KeyAndOrText, FallbackText); + + /// + /// Indicates whether the current is equal to another . + /// + public static bool operator ==(LText left, LText right) => left.Equals(right); + + /// + /// Indicates whether the current is not equal to another . + /// + public static bool operator !=(LText left, LText right) => !(left == right); + /// /// An implicit cast from an to a (see ). /// @@ -73,6 +103,6 @@ public LText(long key, string? fallbackText = null) /// An implicit cast from a text to an value updating the . /// /// The key and/or text. - public static implicit operator LText(string keyAndOrText) => new(keyAndOrText); + public static implicit operator LText(string keyAndOrText) => keyAndOrText is null ? Empty : new(keyAndOrText); } } \ No newline at end of file diff --git a/tests/CoreEx.Cosmos.Test/CoreEx.Cosmos.Test.csproj b/tests/CoreEx.Cosmos.Test/CoreEx.Cosmos.Test.csproj index e48f466b..30e8a451 100644 --- a/tests/CoreEx.Cosmos.Test/CoreEx.Cosmos.Test.csproj +++ b/tests/CoreEx.Cosmos.Test/CoreEx.Cosmos.Test.csproj @@ -22,19 +22,19 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/tests/CoreEx.Solace.Test/CoreEx.Solace.Test.csproj b/tests/CoreEx.Solace.Test/CoreEx.Solace.Test.csproj index 1c11f28f..052c9c6b 100644 --- a/tests/CoreEx.Solace.Test/CoreEx.Solace.Test.csproj +++ b/tests/CoreEx.Solace.Test/CoreEx.Solace.Test.csproj @@ -13,14 +13,14 @@ - - + + - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/tests/CoreEx.Test/CoreEx.Test.csproj b/tests/CoreEx.Test/CoreEx.Test.csproj index c0d14be5..b3ceb5e7 100644 --- a/tests/CoreEx.Test/CoreEx.Test.csproj +++ b/tests/CoreEx.Test/CoreEx.Test.csproj @@ -4,25 +4,26 @@ net6.0 enable false + preview - - + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - + runtime; build; native; contentfiles; analyzers; buildtransitive all - + diff --git a/tests/CoreEx.Test/Framework/Abstractions/Reflection/PropertyExpressionTest.cs b/tests/CoreEx.Test/Framework/Abstractions/Reflection/PropertyExpressionTest.cs index b748ef28..94a70729 100644 --- a/tests/CoreEx.Test/Framework/Abstractions/Reflection/PropertyExpressionTest.cs +++ b/tests/CoreEx.Test/Framework/Abstractions/Reflection/PropertyExpressionTest.cs @@ -16,6 +16,7 @@ public void Create() Assert.That(pe1.Name, Is.EqualTo("Id")); Assert.That(pe1.JsonName, Is.EqualTo("id")); Assert.That((string)pe1.Text, Is.EqualTo("Identifier")); + Assert.That(pe1.Text.KeyAndOrText, Is.EqualTo("CoreEx.Test.Framework.Abstractions.Reflection.Person.Id")); Assert.That(pe1.IsJsonSerializable, Is.True); }); diff --git a/tests/CoreEx.Test/Framework/Results/ValidationExtensionsTest.cs b/tests/CoreEx.Test/Framework/Results/ValidationExtensionsTest.cs index 12497bec..b51a8f0c 100644 --- a/tests/CoreEx.Test/Framework/Results/ValidationExtensionsTest.cs +++ b/tests/CoreEx.Test/Framework/Results/ValidationExtensionsTest.cs @@ -78,6 +78,7 @@ public async Task Validation_Failure_Entity_IValidationResult_InValid() [Test] public async Task Validation_Success_Other_Value() { + 1.Validate().Mandatory().CompareValue(CompareOperator.LessThanEqual, 10); var r = await Result.Go().ValidatesAsync(1, v => v.Mandatory().CompareValue(CompareOperator.LessThanEqual, 10)); Assert.That(r.IsSuccess, Is.True); } @@ -90,6 +91,28 @@ public async Task Validation_Failure_Other_Value() Assert.That(r.Error, Is.Not.Null.And.Message.EqualTo("A data validation error occurred. [id: Identifier must be less than or equal to 10.]")); } + [Test] + public async Task Validation_Success_Other_String() + { + string? email = "a@b"; + + IPropertyRule, string> x = email.Validate().Email(); + + var r = await Result.Go().ValidatesAsync(email, v => v.Email()); + Assert.That(r.IsSuccess, Is.True); + } + + [Test] + public async Task Validation_Success_Other_String2() + { + string email = "a@b"; + + email.Validate().Email(); + + var r = await Result.Go(email).ValidatesAsync(email, v => v.Email()); + Assert.That(r.IsSuccess, Is.True); + } + [Test] public async Task Validation_Simulate_Ensure_And_Validation() { diff --git a/tests/CoreEx.Test/Framework/Validation/ValidatorTest.cs b/tests/CoreEx.Test/Framework/Validation/ValidatorTest.cs index 8f5dc96e..7a922963 100644 --- a/tests/CoreEx.Test/Framework/Validation/ValidatorTest.cs +++ b/tests/CoreEx.Test/Framework/Validation/ValidatorTest.cs @@ -274,7 +274,7 @@ protected override Task OnValidateAsync(ValidationContext cont public class TestEntity { - public List? Items { get; set; } = new List(); + public List? Items { get; set; } = []; public TestItem? Item { get; set; } @@ -306,11 +306,9 @@ public class TestItem2 : IPrimaryKey } - public class TestDataString + public class TestDataString(string name) { - public TestDataString(string name) => Name = name; - - public string Name { get; set; } + public string Name { get; set; } = name; } [Test] @@ -412,7 +410,7 @@ private Result TestInjectValueValidate(PropertyContext cont var type = vxc.GetType(); var mi = type.GetMethod("ValidateAsync")!; - var vc = ((Task>)mi.Invoke(vxc, new object?[] { context.Value, context.CreateValidationArgs(), System.Threading.CancellationToken.None })!).GetAwaiter().GetResult(); + var vc = ((Task>)mi.Invoke(vxc, [context.Value, context.CreateValidationArgs(), System.Threading.CancellationToken.None])!).GetAwaiter().GetResult(); context.Parent.MergeResult(vc); return Result.Success; } diff --git a/tests/CoreEx.Test2/CoreEx.Test2.csproj b/tests/CoreEx.Test2/CoreEx.Test2.csproj index 4df737ca..639f0c68 100644 --- a/tests/CoreEx.Test2/CoreEx.Test2.csproj +++ b/tests/CoreEx.Test2/CoreEx.Test2.csproj @@ -4,19 +4,22 @@ net8.0 enable enable - + preview false true - + - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + all runtime; build; native; contentfiles; analyzers; buildtransitive - @@ -24,6 +27,7 @@ + diff --git a/tests/CoreEx.Test2/Framework/Validation/ValidationExtensionsTest.cs b/tests/CoreEx.Test2/Framework/Validation/ValidationExtensionsTest.cs new file mode 100644 index 00000000..cf67214f --- /dev/null +++ b/tests/CoreEx.Test2/Framework/Validation/ValidationExtensionsTest.cs @@ -0,0 +1,35 @@ +using CoreEx.Results; +using CoreEx.Validation; + +namespace CoreEx.Test2.Framework.Validation +{ + [TestFixture] + public class ValidationExtensionsTest + { + [Test] + public async Task Validation_Success_Other_String() + { + string? email = "a@b"; + var r = await Result.Go(email).ValidatesAsync(email, v => v.Email()); + Assert.That(r.IsSuccess, Is.True); + } + + [Test] + public async Task Validation_Success_Other_String2() + { + string? email = "a@b"; + var r = await ValidateAsync(email); + Assert.That(r.IsSuccess, Is.True); + } + + private async Task> ValidateAsync(string? email2) + { + return await Result.Go().ValidatesAsync(email2, v => + { + var v2 = v; + var v3 = v.Email(); + return v3; + }); + } + } +} \ No newline at end of file diff --git a/tests/CoreEx.TestFunctionIso/CoreEx.TestFunctionIso.csproj b/tests/CoreEx.TestFunctionIso/CoreEx.TestFunctionIso.csproj index c8ac548b..c6075f5a 100644 --- a/tests/CoreEx.TestFunctionIso/CoreEx.TestFunctionIso.csproj +++ b/tests/CoreEx.TestFunctionIso/CoreEx.TestFunctionIso.csproj @@ -14,8 +14,8 @@ - - + +