mirror of https://github.com/abpframework/abp.git
committed by
GitHub
4 changed files with 128 additions and 11 deletions
@ -0,0 +1,26 @@ |
|||||
|
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
||||
|
using Volo.Abp.EntityFrameworkCore.ValueComparers; |
||||
|
using Volo.Abp.EntityFrameworkCore.ValueConverters; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore.Modeling; |
||||
|
|
||||
|
public static class AbpPropertyBuilderExtensions |
||||
|
{ |
||||
|
/// <summary>
|
||||
|
/// Stores the property as a JSON serialized string with snapshot-based
|
||||
|
/// change tracking.
|
||||
|
/// </summary>
|
||||
|
/// <remarks>
|
||||
|
/// A fallback for EF Core providers without owned-JSON (ToJson) or primitive
|
||||
|
/// collection mapping support. The value is converted as a whole: JSON path
|
||||
|
/// querying and partial updates are not available, and the property type must
|
||||
|
/// fully round-trip with System.Text.Json.
|
||||
|
/// </remarks>
|
||||
|
public static PropertyBuilder<TProperty> HasAbpJsonConversion<TProperty>( |
||||
|
this PropertyBuilder<TProperty> propertyBuilder) |
||||
|
{ |
||||
|
return propertyBuilder.HasConversion( |
||||
|
new AbpJsonValueConverter<TProperty>(), |
||||
|
new AbpJsonValueComparer<TProperty>()); |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,26 @@ |
|||||
|
using System.Text.Json; |
||||
|
using Microsoft.EntityFrameworkCore.ChangeTracking; |
||||
|
using Volo.Abp.EntityFrameworkCore.ValueConverters; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore.ValueComparers; |
||||
|
|
||||
|
public class AbpJsonValueComparer<TPropertyType> : ValueComparer<TPropertyType> |
||||
|
{ |
||||
|
public AbpJsonValueComparer() |
||||
|
: base( |
||||
|
(left, right) => Serialize(left) == Serialize(right), |
||||
|
v => Serialize(v).GetHashCode(), |
||||
|
v => Deserialize(Serialize(v))) |
||||
|
{ |
||||
|
} |
||||
|
|
||||
|
private static string Serialize(TPropertyType? value) |
||||
|
{ |
||||
|
return JsonSerializer.Serialize(value, AbpJsonValueConverter<TPropertyType>.SerializeOptions); |
||||
|
} |
||||
|
|
||||
|
private static TPropertyType Deserialize(string value) |
||||
|
{ |
||||
|
return JsonSerializer.Deserialize<TPropertyType>(value, AbpJsonValueConverter<TPropertyType>.DeserializeOptions)!; |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,74 @@ |
|||||
|
using System.Collections.Generic; |
||||
|
using Shouldly; |
||||
|
using Volo.Abp.EntityFrameworkCore.ValueComparers; |
||||
|
using Xunit; |
||||
|
|
||||
|
namespace Volo.Abp.EntityFrameworkCore.ValueComparers; |
||||
|
|
||||
|
public class AbpJsonValueComparer_Tests |
||||
|
{ |
||||
|
[Fact] |
||||
|
public void Should_Compare_Collections_By_Content() |
||||
|
{ |
||||
|
var comparer = new AbpJsonValueComparer<List<string>>(); |
||||
|
|
||||
|
comparer.Equals(new List<string> { "a", "b" }, new List<string> { "a", "b" }).ShouldBeTrue(); |
||||
|
comparer.Equals(new List<string> { "a", "b" }, new List<string> { "b", "a" }).ShouldBeFalse(); |
||||
|
comparer.GetHashCode(new List<string> { "a", "b" }) |
||||
|
.ShouldBe(comparer.GetHashCode(new List<string> { "a", "b" })); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Handle_Null_Values() |
||||
|
{ |
||||
|
var comparer = new AbpJsonValueComparer<List<string>>(); |
||||
|
|
||||
|
comparer.Equals(null, null).ShouldBeTrue(); |
||||
|
comparer.Equals(new List<string>(), null).ShouldBeFalse(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Snapshot_Should_Be_A_Deep_Copy() |
||||
|
{ |
||||
|
var comparer = new AbpJsonValueComparer<List<string>>(); |
||||
|
var original = new List<string> { "a" }; |
||||
|
|
||||
|
var snapshot = (List<string>)comparer.Snapshot(original)!; |
||||
|
original.Add("b"); |
||||
|
|
||||
|
snapshot.ShouldBe(new[] { "a" }); |
||||
|
comparer.Equals(original, snapshot).ShouldBeFalse(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Snapshot_Should_Copy_Nested_Arrays_Of_Typed_Objects() |
||||
|
{ |
||||
|
var comparer = new AbpJsonValueComparer<SampleData>(); |
||||
|
var original = new SampleData { Count = 1, Tags = new[] { "x" } }; |
||||
|
|
||||
|
var snapshot = (SampleData)comparer.Snapshot(original)!; |
||||
|
original.Tags[0] = "changed"; |
||||
|
original.Count = 2; |
||||
|
|
||||
|
snapshot.Tags.ShouldBe(new[] { "x" }); |
||||
|
snapshot.Count.ShouldBe(1); |
||||
|
comparer.Equals(original, snapshot).ShouldBeFalse(); |
||||
|
} |
||||
|
|
||||
|
[Fact] |
||||
|
public void Should_Support_Nullable_Value_Types() |
||||
|
{ |
||||
|
var comparer = new AbpJsonValueComparer<int?>(); |
||||
|
|
||||
|
comparer.Equals(42, 42).ShouldBeTrue(); |
||||
|
comparer.Equals(42, 43).ShouldBeFalse(); |
||||
|
comparer.Equals(null, null).ShouldBeTrue(); |
||||
|
} |
||||
|
|
||||
|
private class SampleData |
||||
|
{ |
||||
|
public int Count { get; set; } |
||||
|
|
||||
|
public string[] Tags { get; set; } = default!; |
||||
|
} |
||||
|
} |
||||
Loading…
Reference in new issue