From c9c8f90bd685657076f2aa4cead8001b872c0f26 Mon Sep 17 00:00:00 2001 From: Matthias Gernand Date: Sat, 28 May 2022 21:37:14 +0200 Subject: [PATCH] Added serializer support fpr primitive value objects. (#15) * Updated packages. * Added serialization support for JSON.NET and System.Text.Json. * Added serializer support for LiteDB. * Added serializer support for MongoDB. * Added EF Core serializer support. * Fixed warnings. --- Fluxera.ValueObject.sln | 70 +++++++++++++++++++ README.md | 2 +- ...era.ValueObject.EntityFrameworkCore.csproj | 37 ++++++++++ .../ModelBuilderExtensions.cs | 53 ++++++++++++++ .../PrimitiveValueObjectConverter.cs | 39 +++++++++++ .../CompositeContractResolver.cs | 56 +++++++++++++++ .../Fluxera.ValueObject.JsonNet.csproj | 37 ++++++++++ .../JsonSerializerSettingsExtensions.cs | 22 ++++++ .../PrimitiveValueObjectContractResolver.cs | 27 +++++++ .../PrimitiveValueObjectConverter.cs | 46 ++++++++++++ .../BsonMapperExtensions.cs | 40 +++++++++++ .../Fluxera.ValueObject.LiteDB.csproj | 37 ++++++++++ .../PrimitiveValueObjectConverter.cs | 51 ++++++++++++++ .../ConventionPackExtensions.cs | 24 +++++++ .../Fluxera.ValueObject.MongoDB.csproj | 37 ++++++++++ .../PrimitiveValueObjectConvention.cs | 31 ++++++++ .../PrimitiveValueObjectSerializer.cs | 47 +++++++++++++ .../EnumerationJsonConverterFactory.cs | 29 ++++++++ .../Fluxera.ValueObject.SystemTextJson.csproj | 37 ++++++++++ .../JsonSerializerOptionsExtensions.cs | 21 ++++++ .../PrimitiveValueObjectConverter.cs | 41 +++++++++++ .../Fluxera.ValueObject.csproj | 2 +- .../PrimitiveValueObject.cs | 1 - .../PrimitiveValueObjectExtensions.cs | 60 ++++++++++++++++ .../DbContextFactory.cs | 21 ++++++ ...bject.EntityFrameworkCore.UnitTests.csproj | 24 +++++++ .../ModelBuilderExtensionsTests.cs | 21 ++++++ .../NoModelCacheKeyFactory.cs | 17 +++++ .../Person.cs | 12 ++++ .../PersonFactory.cs | 22 ++++++ .../StringPrimitive.cs | 10 +++ .../TestDbContext.cs | 67 ++++++++++++++++++ .../ConverterTests.cs | 52 ++++++++++++++ ...uxera.ValueObject.JsonNet.UnitTests.csproj | 21 ++++++ .../StringPrimitive.cs | 10 +++ .../ConverterTests.cs | 45 ++++++++++++ ...luxera.ValueObject.LiteDB.UnitTests.csproj | 22 ++++++ .../StringPrimitive.cs | 10 +++ .../ConverterTests.cs | 47 +++++++++++++ ...uxera.ValueObject.MongoDB.UnitTests.csproj | 22 ++++++ .../StringPrimitive.cs | 10 +++ .../ConverterTests.cs | 46 ++++++++++++ ...alueObject.SystemTextJson.UnitTests.csproj | 22 ++++++ .../StringPrimitive.cs | 10 +++ .../Fluxera.ValueObject.UnitTests.csproj | 10 +-- 45 files changed, 1358 insertions(+), 10 deletions(-) create mode 100644 src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj create mode 100644 src/Fluxera.ValueObject.EntityFrameworkCore/ModelBuilderExtensions.cs create mode 100644 src/Fluxera.ValueObject.EntityFrameworkCore/PrimitiveValueObjectConverter.cs create mode 100644 src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs create mode 100644 src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj create mode 100644 src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs create mode 100644 src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectContractResolver.cs create mode 100644 src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectConverter.cs create mode 100644 src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs create mode 100644 src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj create mode 100644 src/Fluxera.ValueObject.LiteDB/PrimitiveValueObjectConverter.cs create mode 100644 src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs create mode 100644 src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj create mode 100644 src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectConvention.cs create mode 100644 src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectSerializer.cs create mode 100644 src/Fluxera.ValueObject.SystemTextJson/EnumerationJsonConverterFactory.cs create mode 100644 src/Fluxera.ValueObject.SystemTextJson/Fluxera.ValueObject.SystemTextJson.csproj create mode 100644 src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs create mode 100644 src/Fluxera.ValueObject.SystemTextJson/PrimitiveValueObjectConverter.cs create mode 100644 src/Fluxera.ValueObject/PrimitiveValueObjectExtensions.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/DbContextFactory.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests.csproj create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/ModelBuilderExtensionsTests.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/NoModelCacheKeyFactory.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Person.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/PersonFactory.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/StringPrimitive.cs create mode 100644 tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/TestDbContext.cs create mode 100644 tests/Fluxera.ValueObject.JsonNet.UnitTests/ConverterTests.cs create mode 100644 tests/Fluxera.ValueObject.JsonNet.UnitTests/Fluxera.ValueObject.JsonNet.UnitTests.csproj create mode 100644 tests/Fluxera.ValueObject.JsonNet.UnitTests/StringPrimitive.cs create mode 100644 tests/Fluxera.ValueObject.LiteDB.UnitTests/ConverterTests.cs create mode 100644 tests/Fluxera.ValueObject.LiteDB.UnitTests/Fluxera.ValueObject.LiteDB.UnitTests.csproj create mode 100644 tests/Fluxera.ValueObject.LiteDB.UnitTests/StringPrimitive.cs create mode 100644 tests/Fluxera.ValueObject.MongoDB.UnitTests/ConverterTests.cs create mode 100644 tests/Fluxera.ValueObject.MongoDB.UnitTests/Fluxera.ValueObject.MongoDB.UnitTests.csproj create mode 100644 tests/Fluxera.ValueObject.MongoDB.UnitTests/StringPrimitive.cs create mode 100644 tests/Fluxera.ValueObject.SystemTextJson.UnitTests/ConverterTests.cs create mode 100644 tests/Fluxera.ValueObject.SystemTextJson.UnitTests/Fluxera.ValueObject.SystemTextJson.UnitTests.csproj create mode 100644 tests/Fluxera.ValueObject.SystemTextJson.UnitTests/StringPrimitive.cs diff --git a/Fluxera.ValueObject.sln b/Fluxera.ValueObject.sln index fa7521a..cff34f9 100644 --- a/Fluxera.ValueObject.sln +++ b/Fluxera.ValueObject.sln @@ -27,6 +27,26 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluxera.ValueObject", "src\ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Fluxera.ValueObject.UnitTests", "tests\Fluxera.ValueObject.UnitTests\Fluxera.ValueObject.UnitTests.csproj", "{6F6CAF72-1D71-434B-AD09-7A9216669502}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.JsonNet", "src\Fluxera.ValueObject.JsonNet\Fluxera.ValueObject.JsonNet.csproj", "{FA775AE5-1B74-4C42-94CF-03F3BD54E9F8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.JsonNet.UnitTests", "tests\Fluxera.ValueObject.JsonNet.UnitTests\Fluxera.ValueObject.JsonNet.UnitTests.csproj", "{F92B3AEE-E3CF-49FF-857B-7205A7FD08EF}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.SystemTextJson", "src\Fluxera.ValueObject.SystemTextJson\Fluxera.ValueObject.SystemTextJson.csproj", "{78B29A90-2BD9-4603-B3E2-CBB71DD59BEE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.SystemTextJson.UnitTests", "tests\Fluxera.ValueObject.SystemTextJson.UnitTests\Fluxera.ValueObject.SystemTextJson.UnitTests.csproj", "{4B5548EC-9CFF-4F83-9FDF-1831D439DB6B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.LiteDB", "src\Fluxera.ValueObject.LiteDB\Fluxera.ValueObject.LiteDB.csproj", "{ACAAD773-D974-41DA-A04C-9A6B06766D82}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.LiteDB.UnitTests", "tests\Fluxera.ValueObject.LiteDB.UnitTests\Fluxera.ValueObject.LiteDB.UnitTests.csproj", "{5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.MongoDB", "src\Fluxera.ValueObject.MongoDB\Fluxera.ValueObject.MongoDB.csproj", "{8452ADA6-51E7-4E69-A83C-7EBC822E6AAB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.MongoDB.UnitTests", "tests\Fluxera.ValueObject.MongoDB.UnitTests\Fluxera.ValueObject.MongoDB.UnitTests.csproj", "{75A3F24F-590F-457B-B031-B3A41FB51C02}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.EntityFrameworkCore", "src\Fluxera.ValueObject.EntityFrameworkCore\Fluxera.ValueObject.EntityFrameworkCore.csproj", "{AD7A0B26-A63A-47DC-897B-781206B084AC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fluxera.ValueObject.EntityFrameworkCore.UnitTests", "tests\Fluxera.ValueObject.EntityFrameworkCore.UnitTests\Fluxera.ValueObject.EntityFrameworkCore.UnitTests.csproj", "{1E08934F-F3FF-498A-B660-18E6C7C0958B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -41,6 +61,46 @@ Global {6F6CAF72-1D71-434B-AD09-7A9216669502}.Debug|Any CPU.Build.0 = Debug|Any CPU {6F6CAF72-1D71-434B-AD09-7A9216669502}.Release|Any CPU.ActiveCfg = Release|Any CPU {6F6CAF72-1D71-434B-AD09-7A9216669502}.Release|Any CPU.Build.0 = Release|Any CPU + {FA775AE5-1B74-4C42-94CF-03F3BD54E9F8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FA775AE5-1B74-4C42-94CF-03F3BD54E9F8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FA775AE5-1B74-4C42-94CF-03F3BD54E9F8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FA775AE5-1B74-4C42-94CF-03F3BD54E9F8}.Release|Any CPU.Build.0 = Release|Any CPU + {F92B3AEE-E3CF-49FF-857B-7205A7FD08EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F92B3AEE-E3CF-49FF-857B-7205A7FD08EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F92B3AEE-E3CF-49FF-857B-7205A7FD08EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F92B3AEE-E3CF-49FF-857B-7205A7FD08EF}.Release|Any CPU.Build.0 = Release|Any CPU + {78B29A90-2BD9-4603-B3E2-CBB71DD59BEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {78B29A90-2BD9-4603-B3E2-CBB71DD59BEE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {78B29A90-2BD9-4603-B3E2-CBB71DD59BEE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {78B29A90-2BD9-4603-B3E2-CBB71DD59BEE}.Release|Any CPU.Build.0 = Release|Any CPU + {4B5548EC-9CFF-4F83-9FDF-1831D439DB6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4B5548EC-9CFF-4F83-9FDF-1831D439DB6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4B5548EC-9CFF-4F83-9FDF-1831D439DB6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4B5548EC-9CFF-4F83-9FDF-1831D439DB6B}.Release|Any CPU.Build.0 = Release|Any CPU + {ACAAD773-D974-41DA-A04C-9A6B06766D82}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ACAAD773-D974-41DA-A04C-9A6B06766D82}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ACAAD773-D974-41DA-A04C-9A6B06766D82}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ACAAD773-D974-41DA-A04C-9A6B06766D82}.Release|Any CPU.Build.0 = Release|Any CPU + {5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788}.Release|Any CPU.Build.0 = Release|Any CPU + {8452ADA6-51E7-4E69-A83C-7EBC822E6AAB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8452ADA6-51E7-4E69-A83C-7EBC822E6AAB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8452ADA6-51E7-4E69-A83C-7EBC822E6AAB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8452ADA6-51E7-4E69-A83C-7EBC822E6AAB}.Release|Any CPU.Build.0 = Release|Any CPU + {75A3F24F-590F-457B-B031-B3A41FB51C02}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {75A3F24F-590F-457B-B031-B3A41FB51C02}.Debug|Any CPU.Build.0 = Debug|Any CPU + {75A3F24F-590F-457B-B031-B3A41FB51C02}.Release|Any CPU.ActiveCfg = Release|Any CPU + {75A3F24F-590F-457B-B031-B3A41FB51C02}.Release|Any CPU.Build.0 = Release|Any CPU + {AD7A0B26-A63A-47DC-897B-781206B084AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD7A0B26-A63A-47DC-897B-781206B084AC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD7A0B26-A63A-47DC-897B-781206B084AC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD7A0B26-A63A-47DC-897B-781206B084AC}.Release|Any CPU.Build.0 = Release|Any CPU + {1E08934F-F3FF-498A-B660-18E6C7C0958B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1E08934F-F3FF-498A-B660-18E6C7C0958B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1E08934F-F3FF-498A-B660-18E6C7C0958B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1E08934F-F3FF-498A-B660-18E6C7C0958B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -48,6 +108,16 @@ Global GlobalSection(NestedProjects) = preSolution {0112DE42-7C32-4ECC-A5C1-6839C63A689C} = {DF28D730-99B3-4811-AAC7-725A73B3F668} {6F6CAF72-1D71-434B-AD09-7A9216669502} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + {FA775AE5-1B74-4C42-94CF-03F3BD54E9F8} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {F92B3AEE-E3CF-49FF-857B-7205A7FD08EF} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + {78B29A90-2BD9-4603-B3E2-CBB71DD59BEE} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {4B5548EC-9CFF-4F83-9FDF-1831D439DB6B} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + {ACAAD773-D974-41DA-A04C-9A6B06766D82} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {5A4B736C-AEF7-4CF0-A27A-7E8ACD88B788} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + {8452ADA6-51E7-4E69-A83C-7EBC822E6AAB} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {75A3F24F-590F-457B-B031-B3A41FB51C02} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} + {AD7A0B26-A63A-47DC-897B-781206B084AC} = {DF28D730-99B3-4811-AAC7-725A73B3F668} + {1E08934F-F3FF-498A-B660-18E6C7C0958B} = {F18D2D58-282C-4D93-8D9A-3A76CF98F018} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D97BF2AF-6E68-4E88-BF70-773A86E26013} diff --git a/README.md b/README.md index 8fd2db2..97ee778 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ [![Build Status](https://dev.azure.com/fluxera/Foundation/_apis/build/status/GitHub/fluxera.Fluxera.ValueObject?branchName=main)](https://dev.azure.com/fluxera/Foundation/_build/latest?definitionId=63&branchName=main) # Fluxera.ValueObject -A value objects library. +A value object implementation. This library helps in implementing **Value Object** classes in the context of **Domain-Driven Design**. A **Value Object** has several traits, some of which this library provides. diff --git a/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj b/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj new file mode 100644 index 0000000..939ac82 --- /dev/null +++ b/src/Fluxera.ValueObject.EntityFrameworkCore/Fluxera.ValueObject.EntityFrameworkCore.csproj @@ -0,0 +1,37 @@ + + + + net6.0 + + + + Fluxera.ValueObject.EntityFrameworkCore + A libary that provides serializer support for EF Core for value objects. + fluxera;library;ddd;value-object;ef-core + + + + + true + \ + + + true + \ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + \ No newline at end of file diff --git a/src/Fluxera.ValueObject.EntityFrameworkCore/ModelBuilderExtensions.cs b/src/Fluxera.ValueObject.EntityFrameworkCore/ModelBuilderExtensions.cs new file mode 100644 index 0000000..7973213 --- /dev/null +++ b/src/Fluxera.ValueObject.EntityFrameworkCore/ModelBuilderExtensions.cs @@ -0,0 +1,53 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Reflection; + using Fluxera.Guards; + using JetBrains.Annotations; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Metadata; + using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + + /// + /// Extension methods for the type. + /// + [PublicAPI] + public static class ModelBuilderExtensions + { + /// + /// Configure the module builder to use the . + /// + /// + public static void UsePrimitiveValueObject(this ModelBuilder modelBuilder) + { + Guard.Against.Null(modelBuilder, nameof(modelBuilder)); + + foreach(IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes()) + { + IEnumerable properties = entityType + .ClrType + .GetProperties() + .Where(type => type.PropertyType.IsPrimitiveValueObject()); + + foreach(PropertyInfo property in properties) + { + Type enumerationType = property.PropertyType; + Type valueType = enumerationType.GetValueType(); + + Type converterTypeTemplate = typeof(PrimitiveValueObjectConverter<,>); + + Type converterType = converterTypeTemplate.MakeGenericType(enumerationType, valueType); + + ValueConverter converter = (ValueConverter)Activator.CreateInstance(converterType); + + modelBuilder + .Entity(entityType.ClrType) + .Property(property.Name) + .HasConversion(converter); + } + } + } + } +} diff --git a/src/Fluxera.ValueObject.EntityFrameworkCore/PrimitiveValueObjectConverter.cs b/src/Fluxera.ValueObject.EntityFrameworkCore/PrimitiveValueObjectConverter.cs new file mode 100644 index 0000000..d641e39 --- /dev/null +++ b/src/Fluxera.ValueObject.EntityFrameworkCore/PrimitiveValueObjectConverter.cs @@ -0,0 +1,39 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore +{ + using System; + using System.Reflection; + using JetBrains.Annotations; + using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + + /// + [PublicAPI] + public sealed class PrimitiveValueObjectConverter : ValueConverter + where TValueObject : PrimitiveValueObject + where TValue : IComparable + { + /// + /// Initializes a new instance of the type. + /// + public PrimitiveValueObjectConverter() + : base(valueObject => Serialize(valueObject), value => Deserialize(value)) + { + } + + private static TValue Serialize(TValueObject valueObject) + { + TValue value = valueObject.Value; + return value; + } + + private static TValueObject Deserialize(TValue value) + { + if(value is null) + { + return null; + } + + object instance = Activator.CreateInstance(typeof(TValueObject), BindingFlags.Public | BindingFlags.Instance, null, new object[] { value }, null); + return (TValueObject)instance; + } + } +} diff --git a/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs b/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs new file mode 100644 index 0000000..d8aaa9b --- /dev/null +++ b/src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs @@ -0,0 +1,56 @@ +namespace Fluxera.ValueObject.JsonNet +{ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Linq; + using Fluxera.Guards; + using JetBrains.Annotations; + using Newtonsoft.Json.Serialization; + + /// + /// A that allows to have multiple other resolver instances added. + /// + [PublicAPI] + public sealed class CompositeContractResolver : IContractResolver, IEnumerable + { + private readonly IList contractResolvers = new List(); + private readonly DefaultContractResolver defaultContractResolver = new DefaultContractResolver(); + + /// + public JsonContract ResolveContract(Type type) + { + return this.contractResolvers + .Select(x => x.ResolveContract(type)) + .FirstOrDefault(); + } + + /// + public IEnumerator GetEnumerator() + { + return this.contractResolvers.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Add a resolver instance. + /// + /// + public void Add(IContractResolver contractResolver) + { + Guard.Against.Null(contractResolver); + + if(this.contractResolvers.Contains(this.defaultContractResolver)) + { + this.contractResolvers.Remove(this.defaultContractResolver); + } + + this.contractResolvers.Add(contractResolver); + this.contractResolvers.Add(this.defaultContractResolver); + } + } +} diff --git a/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj b/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj new file mode 100644 index 0000000..7a6eacd --- /dev/null +++ b/src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.1 + + + + Fluxera.ValueObject.JsonNet + A libary that provides serializer support for JSON.NET for value objects. + fluxera;library;ddd;value-object;json + + + + + true + \ + + + true + \ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs b/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs new file mode 100644 index 0000000..3250680 --- /dev/null +++ b/src/Fluxera.ValueObject.JsonNet/JsonSerializerSettingsExtensions.cs @@ -0,0 +1,22 @@ +namespace Fluxera.ValueObject.JsonNet +{ + using Newtonsoft.Json; + + /// + /// Extension methods for the type. + /// + public static class JsonSerializerSettingsExtensions + { + /// + /// Configure the serializer to use the . + /// + /// + public static void UsePrimitiveValueObject(this JsonSerializerSettings settings) + { + settings.ContractResolver = new CompositeContractResolver + { + new PrimitiveValueObjectContractResolver() + }; + } + } +} diff --git a/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectContractResolver.cs b/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectContractResolver.cs new file mode 100644 index 0000000..fd87fce --- /dev/null +++ b/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectContractResolver.cs @@ -0,0 +1,27 @@ +namespace Fluxera.ValueObject.JsonNet +{ + using System; + using JetBrains.Annotations; + using Newtonsoft.Json; + using Newtonsoft.Json.Serialization; + + /// + [PublicAPI] + public sealed class PrimitiveValueObjectContractResolver : DefaultContractResolver + { + /// + protected override JsonConverter ResolveContractConverter(Type objectType) + { + if(objectType.IsPrimitiveValueObject()) + { + Type valueType = objectType.GetValueType(); + Type converterTypeTemplate = typeof(PrimitiveValueObjectConverter<,>); + Type converterType = converterTypeTemplate.MakeGenericType(objectType, valueType); + + return (JsonConverter)Activator.CreateInstance(converterType); + } + + return base.ResolveContractConverter(objectType); + } + } +} diff --git a/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectConverter.cs b/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectConverter.cs new file mode 100644 index 0000000..c857826 --- /dev/null +++ b/src/Fluxera.ValueObject.JsonNet/PrimitiveValueObjectConverter.cs @@ -0,0 +1,46 @@ +namespace Fluxera.ValueObject.JsonNet +{ + using System; + using System.Reflection; + using JetBrains.Annotations; + using Newtonsoft.Json; + + /// + [PublicAPI] + public sealed class PrimitiveValueObjectConverter : JsonConverter + where TValueObject : PrimitiveValueObject + where TValue : IComparable + { + /// + public override bool CanWrite => true; + + /// + public override bool CanRead => true; + + /// + public override void WriteJson(JsonWriter writer, TValueObject value, JsonSerializer serializer) + { + if(value is null) + { + writer.WriteNull(); + } + else + { + writer.WriteValue(value.Value); + } + } + + /// + public override TValueObject ReadJson(JsonReader reader, Type objectType, TValueObject existingValue, bool hasExistingValue, JsonSerializer serializer) + { + if(reader.TokenType == JsonToken.Null) + { + return null; + } + + TValue value = serializer.Deserialize(reader); + object instance = Activator.CreateInstance(objectType, BindingFlags.Public | BindingFlags.Instance, null, new object[] { value }, null); + return (TValueObject)instance; + } + } +} diff --git a/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs b/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs new file mode 100644 index 0000000..5d8bf62 --- /dev/null +++ b/src/Fluxera.ValueObject.LiteDB/BsonMapperExtensions.cs @@ -0,0 +1,40 @@ +namespace Fluxera.ValueObject.LiteDB +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Fluxera.Guards; + using global::LiteDB; + using JetBrains.Annotations; + + /// + /// Extension methods for the type. + /// + [PublicAPI] + public static class BsonMapperExtensions + { + /// + /// Configure the mapper to use the . + /// + /// + /// + public static BsonMapper UsePrimitiveValueObject(this BsonMapper mapper) + { + Guard.Against.Null(mapper); + + IEnumerable primitiveValueObjectTypes = AppDomain.CurrentDomain + .GetAssemblies() + .SelectMany(x => x.GetTypes()) + .Where(x => x.IsPrimitiveValueObject()); + + foreach(Type primitiveValueObjectType in primitiveValueObjectTypes) + { + mapper.RegisterType(primitiveValueObjectType, + PrimitiveValueObjectConverter.Serialize(primitiveValueObjectType), + PrimitiveValueObjectConverter.Deserialize(primitiveValueObjectType)); + } + + return mapper; + } + } +} diff --git a/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj b/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj new file mode 100644 index 0000000..763ad08 --- /dev/null +++ b/src/Fluxera.ValueObject.LiteDB/Fluxera.ValueObject.LiteDB.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.1 + + + + Fluxera.ValueObject.LiteDB + A libary that provides serializer support for LiteDB for value objects. + fluxera;library;ddd;value-object;json;litedb + + + + + true + \ + + + true + \ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/Fluxera.ValueObject.LiteDB/PrimitiveValueObjectConverter.cs b/src/Fluxera.ValueObject.LiteDB/PrimitiveValueObjectConverter.cs new file mode 100644 index 0000000..9421e37 --- /dev/null +++ b/src/Fluxera.ValueObject.LiteDB/PrimitiveValueObjectConverter.cs @@ -0,0 +1,51 @@ +namespace Fluxera.ValueObject.LiteDB +{ + using System; + using System.Reflection; + using global::LiteDB; + using JetBrains.Annotations; + + /// + /// A converter for primitive value objects. + /// + [PublicAPI] + public static class PrimitiveValueObjectConverter + { + /// + /// Serialize the given primitive value object instance. + /// + /// + /// + public static Func Serialize(Type primitiveValueObjectType) + { + return obj => + { + PropertyInfo property = primitiveValueObjectType.GetProperty("Value", BindingFlags.Public | BindingFlags.Instance); + object value = property?.GetValue(obj); + + BsonValue bsonValue = new BsonValue(value); + return bsonValue; + }; + } + + /// + /// Deserialize a primitive value object instance from the given bson value. + /// + /// + /// + public static Func Deserialize(Type primitiveValueObjectType) + { + return bson => + { + if(bson.IsNull) + { + return null; + } + + object value = bson.RawValue; + object instance = Activator.CreateInstance(primitiveValueObjectType, BindingFlags.Public | BindingFlags.Instance, null, new object[] { value }, null); + return instance; + }; + } + } +} diff --git a/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs b/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs new file mode 100644 index 0000000..77257bc --- /dev/null +++ b/src/Fluxera.ValueObject.MongoDB/ConventionPackExtensions.cs @@ -0,0 +1,24 @@ +namespace Fluxera.ValueObject.MongoDB +{ + using global::MongoDB.Bson.Serialization.Conventions; + using JetBrains.Annotations; + + /// + /// Extension methods for the type. + /// + [PublicAPI] + public static class ConventionPackExtensions + { + /// + /// Configure the serializer to use the . + /// + /// + /// + public static ConventionPack UsePrimitiveValueObject(this ConventionPack pack) + { + pack.Add(new PrimitiveValueObjectConvention()); + + return pack; + } + } +} diff --git a/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj b/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj new file mode 100644 index 0000000..c4111a4 --- /dev/null +++ b/src/Fluxera.ValueObject.MongoDB/Fluxera.ValueObject.MongoDB.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.1 + + + + Fluxera.ValueObject.MongoDB + A libary that provides serializer support for MongoDB for value objects. + fluxera;library;ddd;value-object;json;mongodb + + + + + true + \ + + + true + \ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectConvention.cs b/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectConvention.cs new file mode 100644 index 0000000..3b1afc9 --- /dev/null +++ b/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectConvention.cs @@ -0,0 +1,31 @@ +namespace Fluxera.ValueObject.MongoDB +{ + using System; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using JetBrains.Annotations; + + /// + /// A convention that enables support for serializing primitive value object properties. + /// + [PublicAPI] + public sealed class PrimitiveValueObjectConvention : ConventionBase, IMemberMapConvention + { + /// + public void Apply(BsonMemberMap memberMap) + { + Type originalMemberType = memberMap.MemberType; + Type memberType = Nullable.GetUnderlyingType(originalMemberType) ?? originalMemberType; + + if(memberType.IsPrimitiveValueObject()) + { + Type valueType = memberType.GetValueType(); + Type serializerTypeTemplate = typeof(PrimitiveValueObjectSerializer<,>); + Type serializerType = serializerTypeTemplate.MakeGenericType(memberType, valueType); + + IBsonSerializer enumerationSerializer = (IBsonSerializer)Activator.CreateInstance(serializerType); + memberMap.SetSerializer(enumerationSerializer); + } + } + } +} diff --git a/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectSerializer.cs b/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectSerializer.cs new file mode 100644 index 0000000..5c714ae --- /dev/null +++ b/src/Fluxera.ValueObject.MongoDB/PrimitiveValueObjectSerializer.cs @@ -0,0 +1,47 @@ +namespace Fluxera.ValueObject.MongoDB +{ + using System; + using System.Reflection; + using global::MongoDB.Bson; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Serializers; + using JetBrains.Annotations; + + /// + /// A serializer that handles instances of primitive value objects. + /// + /// + /// + [PublicAPI] + public sealed class PrimitiveValueObjectSerializer : SerializerBase + where TValueObject : PrimitiveValueObject + where TValue : IComparable + { + /// + public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, TValueObject value) + { + if(value is null) + { + context.Writer.WriteNull(); + } + else + { + BsonSerializer.Serialize(context.Writer, value.Value); + } + } + + /// + public override TValueObject Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + if(context.Reader.CurrentBsonType == BsonType.Null) + { + context.Reader.ReadNull(); + return null; + } + + TValue value = BsonSerializer.Deserialize(context.Reader); + object instance = Activator.CreateInstance(args.NominalType, BindingFlags.Public | BindingFlags.Instance, null, new object[] { value }, null); + return (TValueObject)instance; + } + } +} diff --git a/src/Fluxera.ValueObject.SystemTextJson/EnumerationJsonConverterFactory.cs b/src/Fluxera.ValueObject.SystemTextJson/EnumerationJsonConverterFactory.cs new file mode 100644 index 0000000..6ba7eef --- /dev/null +++ b/src/Fluxera.ValueObject.SystemTextJson/EnumerationJsonConverterFactory.cs @@ -0,0 +1,29 @@ +namespace Fluxera.ValueObject.SystemTextJson +{ + using System; + using System.Text.Json; + using System.Text.Json.Serialization; + using JetBrains.Annotations; + + /// + [PublicAPI] + public sealed class PrimitiveValueObjectJsonConverterFactory : JsonConverterFactory + { + /// + public override bool CanConvert(Type typeToConvert) + { + bool isPrimitiveValueObject = typeToConvert.IsPrimitiveValueObject(); + return isPrimitiveValueObject; + } + + /// + public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + Type valueType = typeToConvert.GetValueType(); + Type converterTypeTemplate = typeof(PrimitiveValueObjectConverter<,>); + Type converterType = converterTypeTemplate.MakeGenericType(typeToConvert, valueType); + + return (JsonConverter)Activator.CreateInstance(converterType); + } + } +} diff --git a/src/Fluxera.ValueObject.SystemTextJson/Fluxera.ValueObject.SystemTextJson.csproj b/src/Fluxera.ValueObject.SystemTextJson/Fluxera.ValueObject.SystemTextJson.csproj new file mode 100644 index 0000000..be2ee65 --- /dev/null +++ b/src/Fluxera.ValueObject.SystemTextJson/Fluxera.ValueObject.SystemTextJson.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.1 + + + + Fluxera.ValueObject.SystemTextJson + A libary that provides serializer support for System.Text.Json for value objects. + fluxera;library;ddd;value-object;json + + + + + true + \ + + + true + \ + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + diff --git a/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs b/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs new file mode 100644 index 0000000..5e53040 --- /dev/null +++ b/src/Fluxera.ValueObject.SystemTextJson/JsonSerializerOptionsExtensions.cs @@ -0,0 +1,21 @@ +namespace Fluxera.ValueObject.SystemTextJson +{ + using System.Text.Json; + using JetBrains.Annotations; + + /// + /// Extension methods for the type. + /// + [PublicAPI] + public static class JsonSerializerOptionsExtensions + { + /// + /// Configures the serializer to use the . + /// + /// + public static void UsePrimitiveValueObject(this JsonSerializerOptions options) + { + options.Converters.Add(new PrimitiveValueObjectJsonConverterFactory()); + } + } +} diff --git a/src/Fluxera.ValueObject.SystemTextJson/PrimitiveValueObjectConverter.cs b/src/Fluxera.ValueObject.SystemTextJson/PrimitiveValueObjectConverter.cs new file mode 100644 index 0000000..4f2a754 --- /dev/null +++ b/src/Fluxera.ValueObject.SystemTextJson/PrimitiveValueObjectConverter.cs @@ -0,0 +1,41 @@ +namespace Fluxera.ValueObject.SystemTextJson +{ + using System; + using System.Reflection; + using System.Text.Json; + using System.Text.Json.Serialization; + using JetBrains.Annotations; + + /// + [PublicAPI] + public sealed class PrimitiveValueObjectConverter : JsonConverter + where TValueObject : PrimitiveValueObject + where TValue : IComparable + { + /// + public override void Write(Utf8JsonWriter writer, TValueObject value, JsonSerializerOptions options) + { + if(value is null) + { + writer.WriteNullValue(); + } + else + { + JsonSerializer.Serialize(writer, value.Value, options); + } + } + + /// + public override TValueObject Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if(reader.TokenType == JsonTokenType.Null) + { + return null; + } + + TValue value = JsonSerializer.Deserialize(ref reader, options); + object instance = Activator.CreateInstance(typeToConvert, BindingFlags.Public | BindingFlags.Instance, null, new object[] { value }, null); + return (TValueObject)instance; + } + } +} diff --git a/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj b/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj index 8798019..ddb5cb4 100644 --- a/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj +++ b/src/Fluxera.ValueObject/Fluxera.ValueObject.csproj @@ -6,7 +6,7 @@ Fluxera.ValueObject - An extendable guard implementation. + A value object implementation. fluxera;library;ddd;value-object diff --git a/src/Fluxera.ValueObject/PrimitiveValueObject.cs b/src/Fluxera.ValueObject/PrimitiveValueObject.cs index fa0e29c..219f544 100644 --- a/src/Fluxera.ValueObject/PrimitiveValueObject.cs +++ b/src/Fluxera.ValueObject/PrimitiveValueObject.cs @@ -44,7 +44,6 @@ protected PrimitiveValueObject(TValue value) /// public TValue Value { get; private set; } - /// public int CompareTo(PrimitiveValueObject other) { diff --git a/src/Fluxera.ValueObject/PrimitiveValueObjectExtensions.cs b/src/Fluxera.ValueObject/PrimitiveValueObjectExtensions.cs new file mode 100644 index 0000000..d431242 --- /dev/null +++ b/src/Fluxera.ValueObject/PrimitiveValueObjectExtensions.cs @@ -0,0 +1,60 @@ +namespace Fluxera.ValueObject +{ + using System; + using JetBrains.Annotations; + + /// + /// Extension methods for the type. + /// + [PublicAPI] + public static class PrimitiveValueObjectExtensions + { + /// + /// Checks the given type if it is an . + /// + /// + /// True, if the type is an enumeration, false otherwise. + public static bool IsPrimitiveValueObject(this Type type) + { + if(type is null || type.IsAbstract || type.IsGenericTypeDefinition) + { + return false; + } + + do + { + if(type.IsGenericType && type.GetGenericTypeDefinition() == typeof(PrimitiveValueObject<,>)) + { + return true; + } + + type = type.BaseType; + } + while(type is not null); + + return false; + } + + /// + /// Gets the type of the generic value parameter from the base type. + /// + /// + /// The type of the value. + public static Type GetValueType(this Type type) + { + do + { + if(type != null && type.IsGenericType && type.GetGenericTypeDefinition() == typeof(PrimitiveValueObject<,>)) + { + Type valueType = type.GetGenericArguments()[1]; + return valueType; + } + + type = type?.BaseType; + } + while(type is not null); + + return null!; + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/DbContextFactory.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/DbContextFactory.cs new file mode 100644 index 0000000..21d66f3 --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/DbContextFactory.cs @@ -0,0 +1,21 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System.Linq; + + public static class DbContextFactory + { + public static TestDbContext Generate(int seedCount) + { + PersonFactory.Initialize(); + + TestDbContext context = new TestDbContext + { + SeedData = PersonFactory.Generate(seedCount).ToArray(), + }; + + context.Database.EnsureDeleted(); + context.Database.EnsureCreated(); + return context; + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests.csproj b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests.csproj new file mode 100644 index 0000000..4fd7164 --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests.csproj @@ -0,0 +1,24 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + + + + + diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/ModelBuilderExtensionsTests.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/ModelBuilderExtensionsTests.cs new file mode 100644 index 0000000..f438131 --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/ModelBuilderExtensionsTests.cs @@ -0,0 +1,21 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System.Collections.Generic; + using System.Linq; + using FluentAssertions; + using NUnit.Framework; + + [TestFixture] + public class ModelBuilderExtensionsTests + { + [Test] + public void ShouldUseNameConverter() + { + int seedCount = 1; + using TestDbContext context = DbContextFactory.Generate(seedCount); + List people = context.Set().ToList(); + + people.Should().BeEquivalentTo(context.SeedData); + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/NoModelCacheKeyFactory.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/NoModelCacheKeyFactory.cs new file mode 100644 index 0000000..fdb92e2 --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/NoModelCacheKeyFactory.cs @@ -0,0 +1,17 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Infrastructure; + + /// + /// Creates a unique cache key every time to prevent caching. + /// + public class NoModelCacheKeyFactory : IModelCacheKeyFactory + { + public object Create(DbContext context) + { + return Guid.NewGuid(); + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Person.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Person.cs new file mode 100644 index 0000000..284195d --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/Person.cs @@ -0,0 +1,12 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System.ComponentModel.DataAnnotations; + + public class Person + { + [Key] + public string Id { get; set; } + + public StringPrimitive StringPrimitive { get; set; } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/PersonFactory.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/PersonFactory.cs new file mode 100644 index 0000000..a5c53cc --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/PersonFactory.cs @@ -0,0 +1,22 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System; + using System.Collections.Generic; + using Bogus; + + public static class PersonFactory + { + public static IList Generate(int count) + { + return new Faker() + .RuleFor(e => e.Id, (f, e) => f.Random.Guid().ToString()) + .RuleFor(e => e.StringPrimitive, (f, e) => new StringPrimitive("12345")) + .Generate(count); + } + + public static void Initialize() + { + Randomizer.Seed = new Random(62392); + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/StringPrimitive.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/StringPrimitive.cs new file mode 100644 index 0000000..3fa47b8 --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/StringPrimitive.cs @@ -0,0 +1,10 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + public sealed class StringPrimitive : PrimitiveValueObject + { + /// + public StringPrimitive(string value) : base(value) + { + } + } +} diff --git a/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/TestDbContext.cs b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/TestDbContext.cs new file mode 100644 index 0000000..8cecbce --- /dev/null +++ b/tests/Fluxera.ValueObject.EntityFrameworkCore.UnitTests/TestDbContext.cs @@ -0,0 +1,67 @@ +namespace Fluxera.ValueObject.EntityFrameworkCore.UnitTests +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Microsoft.EntityFrameworkCore; + using Microsoft.EntityFrameworkCore.Infrastructure; + using Microsoft.EntityFrameworkCore.Metadata.Builders; + using Microsoft.Extensions.Logging; + + public class TestDbContext : DbContext + { + public bool IsLoggingSensitiveData { get; set; } + + public ILoggerFactory LoggerFactory { get; set; } + + public DbSet People { get; set; } + + public IEnumerable SeedData { get; set; } + + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + if(optionsBuilder == null) + { + throw new ArgumentNullException(nameof(optionsBuilder)); + } + + optionsBuilder.UseInMemoryDatabase("TestDatabase"); + + if(this.SeedData != null) + { + optionsBuilder.ReplaceService(); + } + + optionsBuilder.UseLoggerFactory(this.LoggerFactory); + if(this.IsLoggingSensitiveData) + { + optionsBuilder.EnableSensitiveDataLogging(); + } + + base.OnConfiguring(optionsBuilder); + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + if(modelBuilder == null) + { + throw new ArgumentNullException(nameof(modelBuilder)); + } + + if(this.SeedData != null) + { + IEnumerable types = this.SeedData.Select(x => x.GetType()).Distinct(); + foreach(Type type in types) + { + EntityTypeBuilder entityBuilder = modelBuilder.Entity(type); + object[] data = this.SeedData.Where(x => x.GetType() == type).ToArray(); + entityBuilder.HasData(data); + } + } + + modelBuilder.UsePrimitiveValueObject(); + + base.OnModelCreating(modelBuilder); + } + } +} diff --git a/tests/Fluxera.ValueObject.JsonNet.UnitTests/ConverterTests.cs b/tests/Fluxera.ValueObject.JsonNet.UnitTests/ConverterTests.cs new file mode 100644 index 0000000..92d55f3 --- /dev/null +++ b/tests/Fluxera.ValueObject.JsonNet.UnitTests/ConverterTests.cs @@ -0,0 +1,52 @@ +namespace Fluxera.ValueObject.JsonNet.UnitTests +{ + using FluentAssertions; + using Newtonsoft.Json; + using NUnit.Framework; + + [TestFixture] + public class ConverterTests + { + [SetUp] + public void SetUp() + { + JsonConvert.DefaultSettings = () => + { + JsonSerializerSettings settings = new JsonSerializerSettings + { + Formatting = Formatting.None + }; + settings.UsePrimitiveValueObject(); + return settings; + }; + } + + public class TestClass + { + public StringPrimitive StringPrimitive { get; set; } + } + + private static readonly TestClass TestInstance = new TestClass + { + StringPrimitive = new StringPrimitive("12345") + }; + + private static readonly string JsonString = @"{""StringPrimitive"":""12345""}"; + + [Test] + public void ShouldDeserialize() + { + TestClass obj = JsonConvert.DeserializeObject(JsonString); + + obj.StringPrimitive.Should().Be(new StringPrimitive("12345")); + } + + [Test] + public void ShouldSerialize() + { + string json = JsonConvert.SerializeObject(TestInstance, Formatting.None); + + json.Should().Be(JsonString); + } + } +} diff --git a/tests/Fluxera.ValueObject.JsonNet.UnitTests/Fluxera.ValueObject.JsonNet.UnitTests.csproj b/tests/Fluxera.ValueObject.JsonNet.UnitTests/Fluxera.ValueObject.JsonNet.UnitTests.csproj new file mode 100644 index 0000000..8b7adae --- /dev/null +++ b/tests/Fluxera.ValueObject.JsonNet.UnitTests/Fluxera.ValueObject.JsonNet.UnitTests.csproj @@ -0,0 +1,21 @@ + + + + net6.0 + false + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Fluxera.ValueObject.JsonNet.UnitTests/StringPrimitive.cs b/tests/Fluxera.ValueObject.JsonNet.UnitTests/StringPrimitive.cs new file mode 100644 index 0000000..4206168 --- /dev/null +++ b/tests/Fluxera.ValueObject.JsonNet.UnitTests/StringPrimitive.cs @@ -0,0 +1,10 @@ +namespace Fluxera.ValueObject.JsonNet.UnitTests +{ + public sealed class StringPrimitive : PrimitiveValueObject + { + /// + public StringPrimitive(string value) : base(value) + { + } + } +} diff --git a/tests/Fluxera.ValueObject.LiteDB.UnitTests/ConverterTests.cs b/tests/Fluxera.ValueObject.LiteDB.UnitTests/ConverterTests.cs new file mode 100644 index 0000000..7fed99b --- /dev/null +++ b/tests/Fluxera.ValueObject.LiteDB.UnitTests/ConverterTests.cs @@ -0,0 +1,45 @@ +namespace Fluxera.ValueObject.LiteDB.UnitTests +{ + using FluentAssertions; + using global::LiteDB; + using NUnit.Framework; + + [TestFixture] + public class ConverterTests + { + static ConverterTests() + { + BsonMapper.Global.UsePrimitiveValueObject(); + } + + public class TestClass + { + public StringPrimitive StringPrimitive { get; set; } + } + + private static readonly TestClass TestInstance = new TestClass + { + StringPrimitive = new StringPrimitive("12345") + }; + + private static readonly string JsonString = @"{""StringPrimitive"":""12345""}"; + + [Test] + public void ShouldDeserialize() + { + BsonDocument doc = (BsonDocument)JsonSerializer.Deserialize(JsonString); + TestClass obj = BsonMapper.Global.ToObject(doc); + + obj.StringPrimitive.Should().Be(new StringPrimitive("12345")); + } + + [Test] + public void ShouldSerialize() + { + BsonDocument doc = BsonMapper.Global.ToDocument(TestInstance); + string json = JsonSerializer.Serialize(doc); + + json.Should().Be(JsonString); + } + } +} diff --git a/tests/Fluxera.ValueObject.LiteDB.UnitTests/Fluxera.ValueObject.LiteDB.UnitTests.csproj b/tests/Fluxera.ValueObject.LiteDB.UnitTests/Fluxera.ValueObject.LiteDB.UnitTests.csproj new file mode 100644 index 0000000..e4b0692 --- /dev/null +++ b/tests/Fluxera.ValueObject.LiteDB.UnitTests/Fluxera.ValueObject.LiteDB.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + + + diff --git a/tests/Fluxera.ValueObject.LiteDB.UnitTests/StringPrimitive.cs b/tests/Fluxera.ValueObject.LiteDB.UnitTests/StringPrimitive.cs new file mode 100644 index 0000000..276b628 --- /dev/null +++ b/tests/Fluxera.ValueObject.LiteDB.UnitTests/StringPrimitive.cs @@ -0,0 +1,10 @@ +namespace Fluxera.ValueObject.LiteDB.UnitTests +{ + public sealed class StringPrimitive : PrimitiveValueObject + { + /// + public StringPrimitive(string value) : base(value) + { + } + } +} diff --git a/tests/Fluxera.ValueObject.MongoDB.UnitTests/ConverterTests.cs b/tests/Fluxera.ValueObject.MongoDB.UnitTests/ConverterTests.cs new file mode 100644 index 0000000..5f82c36 --- /dev/null +++ b/tests/Fluxera.ValueObject.MongoDB.UnitTests/ConverterTests.cs @@ -0,0 +1,47 @@ +namespace Fluxera.ValueObject.MongoDB.UnitTests +{ + using FluentAssertions; + using global::MongoDB.Bson; + using global::MongoDB.Bson.Serialization; + using global::MongoDB.Bson.Serialization.Conventions; + using NUnit.Framework; + + [TestFixture] + public class ConverterTests + { + static ConverterTests() + { + ConventionPack pack = new ConventionPack(); + pack.UsePrimitiveValueObject(); + ConventionRegistry.Register("ConventionPack", pack, t => true); + } + + public class TestClass + { + public StringPrimitive StringPrimitive { get; set; } + } + + private static readonly TestClass TestInstance = new TestClass + { + StringPrimitive = new StringPrimitive("12345") + }; + + private static readonly string JsonString = @"{ ""StringPrimitive"" : ""12345"" }"; + + [Test] + public void ShouldDeserialize() + { + TestClass obj = BsonSerializer.Deserialize(JsonString); + + obj.StringPrimitive.Should().Be(new StringPrimitive("12345")); + } + + [Test] + public void ShouldSerialize() + { + string json = TestInstance.ToJson(); + + json.Should().Be(JsonString); + } + } +} diff --git a/tests/Fluxera.ValueObject.MongoDB.UnitTests/Fluxera.ValueObject.MongoDB.UnitTests.csproj b/tests/Fluxera.ValueObject.MongoDB.UnitTests/Fluxera.ValueObject.MongoDB.UnitTests.csproj new file mode 100644 index 0000000..e29d7a2 --- /dev/null +++ b/tests/Fluxera.ValueObject.MongoDB.UnitTests/Fluxera.ValueObject.MongoDB.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + + + diff --git a/tests/Fluxera.ValueObject.MongoDB.UnitTests/StringPrimitive.cs b/tests/Fluxera.ValueObject.MongoDB.UnitTests/StringPrimitive.cs new file mode 100644 index 0000000..e0c8a40 --- /dev/null +++ b/tests/Fluxera.ValueObject.MongoDB.UnitTests/StringPrimitive.cs @@ -0,0 +1,10 @@ +namespace Fluxera.ValueObject.MongoDB.UnitTests +{ + public sealed class StringPrimitive : PrimitiveValueObject + { + /// + public StringPrimitive(string value) : base(value) + { + } + } +} diff --git a/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/ConverterTests.cs b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/ConverterTests.cs new file mode 100644 index 0000000..f88ac93 --- /dev/null +++ b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/ConverterTests.cs @@ -0,0 +1,46 @@ +namespace Fluxera.ValueObject.SystemTextJson.UnitTests +{ + using System.Text.Json; + using FluentAssertions; + using NUnit.Framework; + + [TestFixture] + public class ConverterTests + { + private static readonly JsonSerializerOptions options; + + static ConverterTests() + { + options = new JsonSerializerOptions(); + options.UsePrimitiveValueObject(); + } + + public class TestClass + { + public StringPrimitive StringPrimitive { get; set; } + } + + private static readonly TestClass TestInstance = new TestClass + { + StringPrimitive = new StringPrimitive("12345") + }; + + private static readonly string JsonString = @"{""StringPrimitive"":""12345""}"; + + [Test] + public void ShouldDeserialize() + { + TestClass obj = JsonSerializer.Deserialize(JsonString, options); + + obj.StringPrimitive.Should().Be(new StringPrimitive("12345")); + } + + [Test] + public void ShouldSerialize() + { + string json = JsonSerializer.Serialize(TestInstance, options); + + json.Should().Be(JsonString); + } + } +} diff --git a/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/Fluxera.ValueObject.SystemTextJson.UnitTests.csproj b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/Fluxera.ValueObject.SystemTextJson.UnitTests.csproj new file mode 100644 index 0000000..e9f467f --- /dev/null +++ b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/Fluxera.ValueObject.SystemTextJson.UnitTests.csproj @@ -0,0 +1,22 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + + + diff --git a/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/StringPrimitive.cs b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/StringPrimitive.cs new file mode 100644 index 0000000..33c5a8e --- /dev/null +++ b/tests/Fluxera.ValueObject.SystemTextJson.UnitTests/StringPrimitive.cs @@ -0,0 +1,10 @@ +namespace Fluxera.ValueObject.SystemTextJson.UnitTests +{ + public sealed class StringPrimitive : PrimitiveValueObject + { + /// + public StringPrimitive(string value) : base(value) + { + } + } +} diff --git a/tests/Fluxera.ValueObject.UnitTests/Fluxera.ValueObject.UnitTests.csproj b/tests/Fluxera.ValueObject.UnitTests/Fluxera.ValueObject.UnitTests.csproj index fc06c0d..d4af245 100644 --- a/tests/Fluxera.ValueObject.UnitTests/Fluxera.ValueObject.UnitTests.csproj +++ b/tests/Fluxera.ValueObject.UnitTests/Fluxera.ValueObject.UnitTests.csproj @@ -6,14 +6,10 @@ false - - - - - - + + - +