From b3a001fea7fa8e0cba062ee8a90e9250afc3a0cc Mon Sep 17 00:00:00 2001 From: Adrian Hall Date: Fri, 23 May 2025 09:40:38 -0700 Subject: [PATCH] (#360) Convert Add to Replace after conflict --- .../OperationsQueue/OperationsQueueManager.cs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/src/CommunityToolkit.Datasync.Client/Offline/OperationsQueue/OperationsQueueManager.cs b/src/CommunityToolkit.Datasync.Client/Offline/OperationsQueue/OperationsQueueManager.cs index d8ff086..3bab55b 100644 --- a/src/CommunityToolkit.Datasync.Client/Offline/OperationsQueue/OperationsQueueManager.cs +++ b/src/CommunityToolkit.Datasync.Client/Offline/OperationsQueue/OperationsQueueManager.cs @@ -8,7 +8,7 @@ using CommunityToolkit.Datasync.Client.Threading; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; -using System.Net; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; @@ -23,7 +23,7 @@ internal class OperationsQueueManager : IOperationsQueueManager /// /// A lock object for locking against concurrent changes to the queue. /// - private readonly object pushlock = new(); + private readonly Lock pushlock = new(); /// /// The map of valid entities that can be synchronized to the service. @@ -67,24 +67,26 @@ internal OperationsQueueManager(OfflineDbContext context) /// in scope for the operations queue. /// /// A list of values. + [SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "Readability")] internal List GetChangedEntitiesInScope() => ChangeTracker.Entries() - .Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted) - .Where(e => this._entityMap.ContainsKey(e.Metadata.Name.AsNullableEmptyString())) + .Where(e => e.State is EntityState.Added or EntityState.Modified or EntityState.Deleted && this._entityMap.ContainsKey(e.Metadata.Name.AsNullableEmptyString())) .ToList(); /// /// Retrieves the list of synchronizable entities that are available for datasync operations. /// /// - /// An entity is "synchronization ready" if: - /// + /// An entity is "synchronization ready" if: + /// /// * It is a property on this context /// * The property is public and a . /// * The property does not have a specified. /// * The entity type is defined in the model. /// * The entity type has an Id, UpdatedAt, and Version property (according to the ). + /// /// + [SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "Readability")] internal Dictionary GetEntityMap(OfflineDbContext context) { ArgumentNullException.ThrowIfNull(context); @@ -215,11 +217,12 @@ internal IEnumerable GetSynchronizableEntityTypes(IEnumerable allowe /// Determines if the provided property is a synchronizable property. /// /// - /// An entity is "synchronization ready" if: - /// + /// An entity is "synchronization ready" if: + /// /// * It is a property on this context /// * The property is public and a . /// * The property does not have a specified. + /// /// /// The for the property to check. /// true if the property is synchronizable; false otherwise. @@ -243,6 +246,7 @@ internal bool IsSynchronizationEntity(PropertyInfo property) /// The options to use for this push operation. /// A to observe. /// The results of the push operation (asynchronously) + [SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "Readability")] internal async Task PushAsync(IEnumerable entityTypes, PushOptions pushOptions, CancellationToken cancellationToken = default) { ArgumentNullException.ThrowIfNull(entityTypes); @@ -309,6 +313,13 @@ internal async Task PushAsync(IEnumerable entityTypes, PushOpt if (resolution.Result is ConflictResolutionResult.Client) { + // The client entity is the winner, so we need to update the operation and re-queue it. + if (operation.Kind == OperationKind.Add) + { + // The server has an entity and the client is winning, so we need to replace the entity on the server. + operation.Kind = OperationKind.Replace; + } + operation.Item = JsonSerializer.Serialize(resolution.Entity, entityType, DatasyncSerializer.JsonSerializerOptions); operation.State = OperationState.Pending; operation.LastAttempt = DateTimeOffset.UtcNow;