88using CommunityToolkit . Datasync . Client . Threading ;
99using Microsoft . EntityFrameworkCore ;
1010using Microsoft . EntityFrameworkCore . ChangeTracking ;
11- using System . Net ;
11+ using System . Diagnostics . CodeAnalysis ;
1212using System . Reflection ;
1313using System . Text . Json ;
1414
@@ -23,7 +23,7 @@ internal class OperationsQueueManager : IOperationsQueueManager
2323 /// <summary>
2424 /// A lock object for locking against concurrent changes to the queue.
2525 /// </summary>
26- private readonly object pushlock = new ( ) ;
26+ private readonly Lock pushlock = new ( ) ;
2727
2828 /// <summary>
2929 /// The map of valid entities that can be synchronized to the service.
@@ -67,24 +67,26 @@ internal OperationsQueueManager(OfflineDbContext context)
6767 /// in scope for the operations queue.
6868 /// </summary>
6969 /// <returns>A list of <see cref="EntityEntry"/> values.</returns>
70+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
7071 internal List < EntityEntry > GetChangedEntitiesInScope ( )
7172 => 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 ( ) ) )
7474 . ToList ( ) ;
7575
7676 /// <summary>
7777 /// Retrieves the list of synchronizable entities that are available for datasync operations.
7878 /// </summary>
7979 /// <remarks>
80- /// An entity is "synchronization ready" if:
81- ///
80+ /// <para> An entity is "synchronization ready" if:</para>
81+ /// <para>
8282 /// * It is a property on this context
8383 /// * The property is public and a <see cref="DbSet{TEntity}"/>.
8484 /// * The property does not have a <see cref="DoNotSynchronizeAttribute"/> specified.
8585 /// * The entity type is defined in the model.
8686 /// * The entity type has an Id, UpdatedAt, and Version property (according to the <see cref="EntityResolver"/>).
87+ /// </para>
8788 /// </remarks>
89+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
8890 internal Dictionary < string , Type > GetEntityMap ( OfflineDbContext context )
8991 {
9092 ArgumentNullException . ThrowIfNull ( context ) ;
@@ -215,11 +217,12 @@ internal IEnumerable<Type> GetSynchronizableEntityTypes(IEnumerable<Type> allowe
215217 /// Determines if the provided property is a synchronizable property.
216218 /// </summary>
217219 /// <remarks>
218- /// An entity is "synchronization ready" if:
219- ///
220+ /// <para> An entity is "synchronization ready" if:</para>
221+ /// <para>
220222 /// * It is a property on this context
221223 /// * The property is public and a <see cref="DbSet{TEntity}"/>.
222224 /// * The property does not have a <see cref="DoNotSynchronizeAttribute"/> specified.
225+ /// </para>
223226 /// </remarks>
224227 /// <param name="property">The <see cref="PropertyInfo"/> for the property to check.</param>
225228 /// <returns><c>true</c> if the property is synchronizable; <c>false</c> otherwise.</returns>
@@ -243,6 +246,7 @@ internal bool IsSynchronizationEntity(PropertyInfo property)
243246 /// <param name="pushOptions">The options to use for this push operation.</param>
244247 /// <param name="cancellationToken">A <see cref="CancellationToken"/> to observe.</param>
245248 /// <returns>The results of the push operation (asynchronously)</returns>
249+ [ SuppressMessage ( "Style" , "IDE0305:Simplify collection initialization" , Justification = "Readability" ) ]
246250 internal async Task < PushResult > PushAsync ( IEnumerable < Type > entityTypes , PushOptions pushOptions , CancellationToken cancellationToken = default )
247251 {
248252 ArgumentNullException . ThrowIfNull ( entityTypes ) ;
@@ -309,6 +313,13 @@ internal async Task<PushResult> PushAsync(IEnumerable<Type> entityTypes, PushOpt
309313
310314 if ( resolution . Result is ConflictResolutionResult . Client )
311315 {
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+
312323 operation . Item = JsonSerializer . Serialize ( resolution . Entity , entityType , DatasyncSerializer . JsonSerializerOptions ) ;
313324 operation . State = OperationState . Pending ;
314325 operation . LastAttempt = DateTimeOffset . UtcNow ;
0 commit comments