Skip to content

Commit 97556e5

Browse files
authored
(#360) Convert Add to Replace after conflict (#361)
1 parent 881987d commit 97556e5

File tree

1 file changed

+19
-8
lines changed

1 file changed

+19
-8
lines changed

src/CommunityToolkit.Datasync.Client/Offline/OperationsQueue/OperationsQueueManager.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
using CommunityToolkit.Datasync.Client.Threading;
99
using Microsoft.EntityFrameworkCore;
1010
using Microsoft.EntityFrameworkCore.ChangeTracking;
11-
using System.Net;
11+
using System.Diagnostics.CodeAnalysis;
1212
using System.Reflection;
1313
using 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

Comments
 (0)