2025-09-27 18:39:18 +00:00
|
|
|
using System.Text.Json;
|
|
|
|
|
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
|
|
|
|
|
|
|
|
|
namespace Elsa.Persistence.EFCore;
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Compares two objects.
|
|
|
|
|
/// Required to make EF Core change tracking work for complex value converted objects.
|
|
|
|
|
/// </summary>
|
|
|
|
|
/// <remarks>
|
|
|
|
|
/// For objects that implement <see cref="ICloneable"/> and <see cref="IEquatable{T}"/>,
|
|
|
|
|
/// those implementations will be used for cloning and equality.
|
|
|
|
|
/// For plain objects, fall back to deep equality comparison using JSON serialization
|
|
|
|
|
/// (safe, but inefficient).
|
|
|
|
|
/// </remarks>
|
2025-11-12 15:56:31 +00:00
|
|
|
public class JsonValueComparer<T>() : ValueComparer<T>((t1, t2) => DoEquals(t1!, t2!),
|
|
|
|
|
t => DoGetHashCode(t),
|
|
|
|
|
t => DoGetSnapshot(t))
|
|
|
|
|
{
|
2025-09-27 18:39:18 +00:00
|
|
|
|
|
|
|
|
private static string Json(T instance) {
|
|
|
|
|
return JsonSerializer.Serialize(instance);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static T DoGetSnapshot(T instance) {
|
|
|
|
|
|
|
|
|
|
if (instance is ICloneable cloneable)
|
|
|
|
|
return (T)cloneable.Clone();
|
|
|
|
|
|
2025-11-12 15:56:31 +00:00
|
|
|
return JsonSerializer.Deserialize<T>(Json(instance))!;
|
2025-09-27 18:39:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static int DoGetHashCode(T instance) {
|
|
|
|
|
|
|
|
|
|
if (instance is IEquatable<T>)
|
|
|
|
|
return instance.GetHashCode();
|
|
|
|
|
|
|
|
|
|
return Json(instance).GetHashCode();
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool DoEquals(T left, T right) {
|
|
|
|
|
|
|
|
|
|
if (left is IEquatable<T> equatable)
|
|
|
|
|
return equatable.Equals(right);
|
|
|
|
|
|
2025-11-12 15:56:31 +00:00
|
|
|
return Json(left).Equals(Json(right));
|
2025-09-27 18:39:18 +00:00
|
|
|
}
|
|
|
|
|
}
|