diff --git a/README.md b/README.md index 9ebf064f..a6226a71 100644 --- a/README.md +++ b/README.md @@ -222,32 +222,128 @@ Features: * Roslyn Analyzers and CodeFixes help the developers to implement the Value Objects correctly * Allows custom properties and methods * Provides appropriate factory methods for creation of new value objects based on the specified properties/fields -* Allows custom validation of [constructor](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#validation-of-the-constructor-arguments) and [factory method](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#validation-of-the-factory-method-arguments) arguments +* Allows custom [validation](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#validation) of [constructor](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#validation-of-the-constructor-arguments) and [factory method](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#validation-of-the-factory-method-arguments) arguments * Additional features for [simple Value Objects (1 "key"-property/field)](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#simple-value-objects) and [complex Value Objects (2 properties/fields or more)](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#complex-value-objects) * Simple Value Objects: allows cast and type conversion from key-type to Value Object and vice versa -* Simple Value Objects: provides an implementation of `IComparable` if the key-property/field is an `IComparable` or has an `IComparer` * Simple Value Objects: provides an implementation of `IFormattable` if the key-property/field is an `IFormattable` * Provides proper implementation of `Equals`, `GetHashCode`, `ToString` and equality comparison via `==` and `!=` -* [Allows custom equality comparison](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#custom-comparer) +* Provides implementation of `IComparable`, `IComparable`, `IFormattable`, `IParsable` and comparison operators `<`, `<=`, `>`, `>=` +* [Allows custom equality comparison](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#custom-comparer) and [custom comparer](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#custom-comparer) * Handling of [null](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#null-in-factory-methods-yields-null) and [empty strings](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#empty-string-in-factory-methods-yields-null) * [JSON support](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#json-serialization) (`System.Text.Json` and `Newtonsoft.Json`) -* [ASP.NET Core support](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#support-for-aspnet-core-model-binding) (model binding and model validation) +* [Support for Minimal Web Api Parameter Binding and ASP.NET Core Model Binding](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#support-for-minimal-web-api-parameter-binding-and-aspnet-core-model-binding) * [Entity Framework Core support](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#support-for-entity-framework-core) (`ValueConverter`) * [MessagePack support](https://github.com/PawelGerr/Thinktecture.Runtime.Extensions/wiki/Value-Objects#messagepack-serialization) (`IMessagePackFormatter`) -Definition of a value object with 1 custom property `Value`. All other features mentioned above are generated by the [Roslyn Source Generators](https://docs.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview) in the background. +## Simple Value Object + +Definition of 2 value objects, one with 1 `string` property `Value` and the other with an `int`. ```C# [ValueObject] -public partial class ProductName +public sealed partial class ProductName { public string Value { get; } +} - // The member can be a private readoly field as well - //private readonly string _value; +[ValueObject] +public sealed partial class Amount +{ + private readonly int _value; } ``` +After the implementation of the `ProductName`, a Roslyn source generator kicks in and implements the rest. Following API is available from now on. + +```C# +// Factory method for creation of new instances. +// Throws ValidationException if the validation fails +ProductName bread = ProductName.Create("Bread"); + +// Alternatively, using an explicit cast (behaves the same as with Create) +ProductName bread = (ProductName)"Bread"; // is the same as calling "ProductName.Create" + +----------- + +// the same as above but returns a bool instead of throwing an exception (dictionary-style) +bool created = ProductName.TryCreate("Milk", out ProductName milk); + +----------- + +// similar to TryCreate but returns a ValidationResult instead of a boolean. +ValidationResult? validationResult = ProductName.Validate("Milk", out var milk); + +if (validationResult == ValidationResult.Success) +{ + logger.Information("Product name {Name} created", milk); +} +else +{ + logger.Warning("Failed to create product name. Validation result: {ValidationResult}", validationResult.ErrorMessage); +} + +----------- + +// implicit conversion to the type of the key member +string valueOfTheProductName = bread; // "Bread" + +----------- + +// Equality comparison with 'Equals' +// which compares the key members using default or custom 'IEqualityComparer' +bool equal = bread.Equals(bread); + +----------- + +// Equality comparison with '==' and '!=' +bool equal = bread == bread; +bool notEqual = bread != bread; + +----------- + +// Hash code +int hashCode = bread.GetHashCode(); + +----------- + +// 'ToString' implementation +string value = bread.ToString(); // "Bread" + +------------ + +// Implements IParsable which is especially helpful with minimal web apis. +// This feature can be disabled if it doesn't make sense (see ValueObjectAttribute). +bool success = ProductName.TryParse("New product name", null, out var productName); + +------------ + +// Implements "IFormattable" if the key member is an "IFormattable". +// This feature can be disabled if it doesn't make sense (see ValueObjectAttribute). +var amount = Amount.Create(42); +string formattedValue = amount.ToString("000", CultureInfo.InvariantCulture); // "042" + +------------ + +// Implements "IComparable" if the key member is an "IComparable" +// This feature can be disabled if it doesn't make sense (see ValueObjectAttribute). +var amount = Amount.Create(1); +var otherAmount = Amount.Create(2); + +var comparison = amount.CompareTo(otherAmount); // -1 + +// Implements comparison operators (<,<=,>,>=) if the key member has comparison operators itself. +// This feature can be disabled if it doesn't make sense (see ValueObjectAttribute). +var isBigger = amount > otherAmount; + +------------ + +// Implements addition / subtraction / multiplication / division if the key member supports operators +// This feature can be disabled if it doesn't make sense (see ValueObjectAttribute). +var sum = amount + otherAmount; +``` + +## Complex Value Object + Definition of a complex value object with 2 properties and a custom validation of the arguments. ```C#