8
8
using CommunityToolkit . Datasync . Client . Threading ;
9
9
using Microsoft . EntityFrameworkCore ;
10
10
using Microsoft . EntityFrameworkCore . ChangeTracking ;
11
- using System . Net ;
11
+ using System . Diagnostics . CodeAnalysis ;
12
12
using System . Reflection ;
13
13
using System . Text . Json ;
14
14
@@ -23,7 +23,7 @@ internal class OperationsQueueManager : IOperationsQueueManager
23
23
/// <summary>
24
24
/// A lock object for locking against concurrent changes to the queue.
25
25
/// </summary>
26
- private readonly object pushlock = new ( ) ;
26
+ private readonly Lock pushlock = new ( ) ;
27
27
28
28
/// <summary>
29
29
/// The map of valid entities that can be synchronized to the service.
@@ -67,24 +67,26 @@ internal OperationsQueueManager(OfflineDbContext context)
67
67
/// in scope for the operations queue.
68
68
/// </summary>
69
69
/// <returns>A list of <see cref="EntityEntry"/> values.</returns>
70
+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
70
71
internal List < EntityEntry > GetChangedEntitiesInScope ( )
71
72
=> ChangeTracker . Entries ( )
72
- . Where ( e => e . State is EntityState . Added or EntityState . Modified or EntityState . Deleted )
73
- . Where ( e => this . _entityMap . ContainsKey ( e . Metadata . Name . AsNullableEmptyString ( ) ) )
73
+ . Where ( e => e . State is EntityState . Added or EntityState . Modified or EntityState . Deleted && this . _entityMap . ContainsKey ( e . Metadata . Name . AsNullableEmptyString ( ) ) )
74
74
. ToList ( ) ;
75
75
76
76
/// <summary>
77
77
/// Retrieves the list of synchronizable entities that are available for datasync operations.
78
78
/// </summary>
79
79
/// <remarks>
80
- /// An entity is "synchronization ready" if:
81
- ///
80
+ /// <para> An entity is "synchronization ready" if:</para>
81
+ /// <para>
82
82
/// * It is a property on this context
83
83
/// * The property is public and a <see cref="DbSet{TEntity}"/>.
84
84
/// * The property does not have a <see cref="DoNotSynchronizeAttribute"/> specified.
85
85
/// * The entity type is defined in the model.
86
86
/// * The entity type has an Id, UpdatedAt, and Version property (according to the <see cref="EntityResolver"/>).
87
+ /// </para>
87
88
/// </remarks>
89
+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
88
90
internal Dictionary < string , Type > GetEntityMap ( OfflineDbContext context )
89
91
{
90
92
ArgumentNullException . ThrowIfNull ( context ) ;
@@ -215,11 +217,12 @@ internal IEnumerable<Type> GetSynchronizableEntityTypes(IEnumerable<Type> allowe
215
217
/// Determines if the provided property is a synchronizable property.
216
218
/// </summary>
217
219
/// <remarks>
218
- /// An entity is "synchronization ready" if:
219
- ///
220
+ /// <para> An entity is "synchronization ready" if:</para>
221
+ /// <para>
220
222
/// * It is a property on this context
221
223
/// * The property is public and a <see cref="DbSet{TEntity}"/>.
222
224
/// * The property does not have a <see cref="DoNotSynchronizeAttribute"/> specified.
225
+ /// </para>
223
226
/// </remarks>
224
227
/// <param name="property">The <see cref="PropertyInfo"/> for the property to check.</param>
225
228
/// <returns><c>true</c> if the property is synchronizable; <c>false</c> otherwise.</returns>
@@ -243,6 +246,7 @@ internal bool IsSynchronizationEntity(PropertyInfo property)
243
246
/// <param name="pushOptions">The options to use for this push operation.</param>
244
247
/// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
245
248
/// <returns>The results of the push operation (asynchronously)</returns>
249
+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
246
250
internal async Task < PushResult > PushAsync ( IEnumerable < Type > entityTypes , PushOptions pushOptions , CancellationToken cancellationToken = default )
247
251
{
248
252
ArgumentNullException . ThrowIfNull ( entityTypes ) ;
@@ -309,6 +313,13 @@ internal async Task<PushResult> PushAsync(IEnumerable<Type> entityTypes, PushOpt
309
313
310
314
if ( resolution . Result is ConflictResolutionResult . Client )
311
315
{
316
+ // The client entity is the winner, so we need to update the operation and re-queue it.
317
+ if ( operation . Kind == OperationKind . Add )
318
+ {
319
+ // The server has an entity and the client is winning, so we need to replace the entity on the server.
320
+ operation . Kind = OperationKind . Replace ;
321
+ }
322
+
312
323
operation . Item = JsonSerializer . Serialize ( resolution . Entity , entityType , DatasyncSerializer . JsonSerializerOptions ) ;
313
324
operation . State = OperationState . Pending ;
314
325
operation . LastAttempt = DateTimeOffset . UtcNow ;
0 commit comments