@@ -20,8 +20,6 @@ public class RedisCollectionStateManager
2020 DefaultIgnoreCondition = JsonIgnoreCondition . WhenWritingNull ,
2121 } ;
2222
23- private readonly DocumentAttribute _documentAttribute ;
24-
2523 static RedisCollectionStateManager ( )
2624 {
2725 JsonSerializerOptions . Converters . Add ( new GeoLocJsonConverter ( ) ) ;
@@ -34,9 +32,14 @@ static RedisCollectionStateManager()
3432 /// <param name="attr">The document attribute for the type.</param>
3533 public RedisCollectionStateManager ( DocumentAttribute attr )
3634 {
37- _documentAttribute = attr ;
35+ DocumentAttribute = attr ;
3836 }
3937
38+ /// <summary>
39+ /// Gets the DocumentAttribute for the underlying type.
40+ /// </summary>
41+ public DocumentAttribute DocumentAttribute { get ; }
42+
4043 /// <summary>
4144 /// Gets a snapshot from when the collection enumerated.
4245 /// </summary>
@@ -47,19 +50,37 @@ public RedisCollectionStateManager(DocumentAttribute attr)
4750 /// </summary>
4851 internal IDictionary < string , object ? > Data { get ; set ; } = new Dictionary < string , object ? > ( ) ;
4952
53+ /// <summary>
54+ /// Removes the key from the data and snapshot.
55+ /// </summary>
56+ /// <param name="key">The key to remove.</param>
57+ internal void Remove ( string key )
58+ {
59+ Snapshot . Remove ( key ) ;
60+ Data . Remove ( key ) ;
61+ }
62+
63+ /// <summary>
64+ /// Add item to data.
65+ /// </summary>
66+ /// <param name="key">the item's key.</param>
67+ /// <param name="value">the item's value.</param>
68+ internal void InsertIntoData ( string key , object value )
69+ {
70+ Data . Remove ( key ) ;
71+ Data . Add ( key , value ) ;
72+ }
73+
5074 /// <summary>
5175 /// Add item to snapshot.
5276 /// </summary>
5377 /// <param name="key">the item's key.</param>
5478 /// <param name="value">the current value of the item.</param>
5579 internal void InsertIntoSnapshot ( string key , object value )
5680 {
57- if ( Snapshot . ContainsKey ( key ) )
58- {
59- return ;
60- }
81+ Snapshot . Remove ( key ) ;
6182
62- if ( _documentAttribute . StorageType == StorageType . Json )
83+ if ( DocumentAttribute . StorageType == StorageType . Json )
6384 {
6485 var json = JToken . FromObject ( value , Newtonsoft . Json . JsonSerializer . Create ( new JsonSerializerSettings { NullValueHandling = NullValueHandling . Ignore } ) ) ;
6586 Snapshot . Add ( key , json ) ;
@@ -71,14 +92,53 @@ internal void InsertIntoSnapshot(string key, object value)
7192 }
7293 }
7394
95+ /// <summary>
96+ /// Builds a diff for a single object from what's currently in the snapshot.
97+ /// </summary>
98+ /// <param name="key">the key of the object in redis.</param>
99+ /// <param name="value">The current value.</param>
100+ /// <param name="differences">The detected differences.</param>
101+ /// <returns>Whether a diff could be constructed.</returns>
102+ internal bool TryDetectDifferencesSingle ( string key , object value , out IList < IObjectDiff > ? differences )
103+ {
104+ if ( ! Snapshot . ContainsKey ( key ) )
105+ {
106+ differences = null ;
107+ return false ;
108+ }
109+
110+ if ( DocumentAttribute . StorageType == StorageType . Json )
111+ {
112+ var dataJson = JsonSerializer . Serialize ( value , JsonSerializerOptions ) ;
113+ var current = JsonConvert . DeserializeObject < JObject > ( dataJson , new JsonSerializerSettings { NullValueHandling = NullValueHandling . Ignore } ) ;
114+ var snapshot = ( JToken ) Snapshot [ key ] ;
115+ var diff = FindDiff ( current ! , snapshot ) ;
116+ differences = BuildJsonDifference ( diff , "$" ) ;
117+ }
118+ else
119+ {
120+ var dataHash = value . BuildHashSet ( ) ;
121+ var snapshotHash = ( IDictionary < string , string > ) Snapshot [ key ] ;
122+ var deletedKeys = snapshotHash . Keys . Except ( dataHash . Keys ) . Select ( x => new KeyValuePair < string , string > ( x , string . Empty ) ) ;
123+ var modifiedKeys = dataHash . Where ( x =>
124+ ! snapshotHash . Keys . Contains ( x . Key ) || snapshotHash [ x . Key ] != x . Value ) ;
125+ differences = new List < IObjectDiff >
126+ {
127+ new HashDiff ( modifiedKeys , deletedKeys . Select ( x => x . Key ) ) ,
128+ } ;
129+ }
130+
131+ return true ;
132+ }
133+
74134 /// <summary>
75135 /// Detects the differences.
76136 /// </summary>
77137 /// <returns>a difference dictionary.</returns>
78138 internal IDictionary < string , IList < IObjectDiff > > DetectDifferences ( )
79139 {
80140 var res = new Dictionary < string , IList < IObjectDiff > > ( ) ;
81- if ( _documentAttribute . StorageType == StorageType . Json )
141+ if ( DocumentAttribute . StorageType == StorageType . Json )
82142 {
83143 foreach ( var key in Snapshot . Keys )
84144 {
@@ -267,8 +327,12 @@ private static JObject FindDiff(JToken currentObject, JToken snapshotObject)
267327
268328 break ;
269329 default :
270- diff [ "+" ] = currentObject ;
271- diff [ "-" ] = snapshotObject ;
330+ if ( currentObject . ToString ( ) != snapshotObject . ToString ( ) )
331+ {
332+ diff [ "+" ] = currentObject ;
333+ diff [ "-" ] = snapshotObject ;
334+ }
335+
272336 break ;
273337 }
274338
0 commit comments