| | | 1 | | using System.Text.Json; |
| | | 2 | | using Microsoft.EntityFrameworkCore.ChangeTracking; |
| | | 3 | | |
| | | 4 | | namespace Elsa.Persistence.EFCore; |
| | | 5 | | |
| | | 6 | | /// <summary> |
| | | 7 | | /// Compares two objects. |
| | | 8 | | /// Required to make EF Core change tracking work for complex value converted objects. |
| | | 9 | | /// </summary> |
| | | 10 | | /// <remarks> |
| | | 11 | | /// For objects that implement <see cref="ICloneable"/> and <see cref="IEquatable{T}"/>, |
| | | 12 | | /// those implementations will be used for cloning and equality. |
| | | 13 | | /// For plain objects, fall back to deep equality comparison using JSON serialization |
| | | 14 | | /// (safe, but inefficient). |
| | | 15 | | /// </remarks> |
| | 0 | 16 | | public class JsonValueComparer<T>() : ValueComparer<T>((t1, t2) => DoEquals(t1!, t2!), |
| | 0 | 17 | | t => DoGetHashCode(t), |
| | 0 | 18 | | t => DoGetSnapshot(t)) |
| | | 19 | | { |
| | | 20 | | |
| | | 21 | | private static string Json(T instance) { |
| | 0 | 22 | | return JsonSerializer.Serialize(instance); |
| | | 23 | | } |
| | | 24 | | |
| | | 25 | | private static T DoGetSnapshot(T instance) { |
| | | 26 | | |
| | 0 | 27 | | if (instance is ICloneable cloneable) |
| | 0 | 28 | | return (T)cloneable.Clone(); |
| | | 29 | | |
| | 0 | 30 | | return JsonSerializer.Deserialize<T>(Json(instance))!; |
| | | 31 | | } |
| | | 32 | | |
| | | 33 | | private static int DoGetHashCode(T instance) { |
| | | 34 | | |
| | 0 | 35 | | if (instance is IEquatable<T>) |
| | 0 | 36 | | return instance.GetHashCode(); |
| | | 37 | | |
| | 0 | 38 | | return Json(instance).GetHashCode(); |
| | | 39 | | |
| | | 40 | | } |
| | | 41 | | |
| | | 42 | | private static bool DoEquals(T left, T right) { |
| | | 43 | | |
| | 0 | 44 | | if (left is IEquatable<T> equatable) |
| | 0 | 45 | | return equatable.Equals(right); |
| | | 46 | | |
| | 0 | 47 | | return Json(left).Equals(Json(right)); |
| | | 48 | | } |
| | | 49 | | } |