Skip to content

Commit

Permalink
Added serializer support fpr primitive value objects. (#15)
Browse files Browse the repository at this point in the history
* 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.
  • Loading branch information
mgernand authored May 28, 2022
1 parent df6ad76 commit c9c8f90
Show file tree
Hide file tree
Showing 45 changed files with 1,358 additions and 10 deletions.
70 changes: 70 additions & 0 deletions Fluxera.ValueObject.sln
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -41,13 +61,63 @@ 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
EndGlobalSection
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}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
</PropertyGroup>

<PropertyGroup>
<Title>Fluxera.ValueObject.EntityFrameworkCore</Title>
<Description>A libary that provides serializer support for EF Core for value objects.</Description>
<PackageTags>fluxera;library;ddd;value-object;ef-core</PackageTags>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\README.md" Link="Properties\README.md">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\icon.png" Link="Properties\icon.png">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="5.10.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.5" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Fluxera.ValueObject\Fluxera.ValueObject.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// Extension methods for the <see cref="ModelBuilder" /> type.
/// </summary>
[PublicAPI]
public static class ModelBuilderExtensions
{
/// <summary>
/// Configure the module builder to use the <see cref="PrimitiveValueObjectConverter{TValueObject,TValue}" />.
/// </summary>
/// <param name="modelBuilder"></param>
public static void UsePrimitiveValueObject(this ModelBuilder modelBuilder)
{
Guard.Against.Null(modelBuilder, nameof(modelBuilder));

foreach(IMutableEntityType entityType in modelBuilder.Model.GetEntityTypes())
{
IEnumerable<PropertyInfo> 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);
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
namespace Fluxera.ValueObject.EntityFrameworkCore
{
using System;
using System.Reflection;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;

/// <inheritdoc />
[PublicAPI]
public sealed class PrimitiveValueObjectConverter<TValueObject, TValue> : ValueConverter<TValueObject, TValue>
where TValueObject : PrimitiveValueObject<TValueObject, TValue>
where TValue : IComparable
{
/// <summary>
/// Initializes a new instance of the <see cref="PrimitiveValueObjectConverter{TValueObject,TValue}" /> type.
/// </summary>
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;
}
}
}
56 changes: 56 additions & 0 deletions src/Fluxera.ValueObject.JsonNet/CompositeContractResolver.cs
Original file line number Diff line number Diff line change
@@ -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;

/// <summary>
/// A <see cref="IContractResolver" /> that allows to have multiple other resolver instances added.
/// </summary>
[PublicAPI]
public sealed class CompositeContractResolver : IContractResolver, IEnumerable<IContractResolver>
{
private readonly IList<IContractResolver> contractResolvers = new List<IContractResolver>();
private readonly DefaultContractResolver defaultContractResolver = new DefaultContractResolver();

/// <inheritdoc />
public JsonContract ResolveContract(Type type)
{
return this.contractResolvers
.Select(x => x.ResolveContract(type))
.FirstOrDefault();
}

/// <inheritdoc />
public IEnumerator<IContractResolver> GetEnumerator()
{
return this.contractResolvers.GetEnumerator();
}

IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}

/// <summary>
/// Add a resolver instance.
/// </summary>
/// <param name="contractResolver"></param>
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);
}
}
}
37 changes: 37 additions & 0 deletions src/Fluxera.ValueObject.JsonNet/Fluxera.ValueObject.JsonNet.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
</PropertyGroup>

<PropertyGroup>
<Title>Fluxera.ValueObject.JsonNet</Title>
<Description>A libary that provides serializer support for JSON.NET for value objects.</Description>
<PackageTags>fluxera;library;ddd;value-object;json</PackageTags>
</PropertyGroup>

<ItemGroup>
<None Include="..\..\README.md" Link="Properties\README.md">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
<None Include="..\..\icon.png" Link="Properties\icon.png">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
</None>
</ItemGroup>

<ItemGroup>
<PackageReference Include="GitVersion.MsBuild" Version="5.10.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="JetBrains.Annotations" Version="2022.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\Fluxera.ValueObject\Fluxera.ValueObject.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace Fluxera.ValueObject.JsonNet
{
using Newtonsoft.Json;

/// <summary>
/// Extension methods for the <see cref="JsonSerializerSettings" /> type.
/// </summary>
public static class JsonSerializerSettingsExtensions
{
/// <summary>
/// Configure the serializer to use the <see cref="PrimitiveValueObjectConverter{TValueObject,TValue}" />.
/// </summary>
/// <param name="settings"></param>
public static void UsePrimitiveValueObject(this JsonSerializerSettings settings)
{
settings.ContractResolver = new CompositeContractResolver
{
new PrimitiveValueObjectContractResolver()
};
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace Fluxera.ValueObject.JsonNet
{
using System;
using JetBrains.Annotations;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;

/// <inheritdoc />
[PublicAPI]
public sealed class PrimitiveValueObjectContractResolver : DefaultContractResolver
{
/// <inheritdoc />
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);
}
}
}
Loading

0 comments on commit c9c8f90

Please sign in to comment.