Skip to content

Commit 1d02110

Browse files
committed
(#384) Added SynchronizationProgress event
1 parent c35d90c commit 1d02110

File tree

3 files changed

+97
-2
lines changed

3 files changed

+97
-2
lines changed

src/CommunityToolkit.Datasync.Client/Offline/OfflineDbContext.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,12 @@ public abstract partial class OfflineDbContext : DbContext
9999
/// </summary>
100100
internal OperationsQueueManager QueueManager { get; }
101101

102+
/// <summary>
103+
/// An event delegate that allows the app to monitor synchronization events.
104+
/// </summary>
105+
/// <remarks>This event can be called from background threads.</remarks>
106+
public event EventHandler<SynchronizationEventArgs>? SynchronizationProgress;
107+
102108
/// <summary>
103109
/// Initializes a new instance of the <see cref="OfflineDbContext" /> class. The
104110
/// <see cref="OnConfiguring(DbContextOptionsBuilder)" /> method will be called to
@@ -561,6 +567,15 @@ public async Task<int> SaveChangesAsync(bool acceptAllChangesOnSuccess, bool add
561567
return await base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken).ConfigureAwait(false);
562568
}
563569

570+
/// <summary>
571+
/// Sends a synchronization event to the consumers.
572+
/// </summary>
573+
/// <param name="eventArgs">The event arguments.</param>
574+
internal void SendSynchronizationEvent(SynchronizationEventArgs eventArgs)
575+
{
576+
SynchronizationProgress?.Invoke(this, eventArgs);
577+
}
578+
564579
#region IDisposable
565580
/// <summary>
566581
/// Ensure that the context has not been disposed.

src/CommunityToolkit.Datasync.Client/Offline/Operations/PullOperationManager.cs

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,15 @@ public async Task<PullResult> ExecuteAsync(IEnumerable<PullRequest> requests, Pu
105105
}
106106
}
107107

108+
context.SendSynchronizationEvent(new SynchronizationEventArgs()
109+
{
110+
EventType = SynchronizationEventType.ItemsCommitted,
111+
EntityType = pullResponse.EntityType,
112+
ItemsProcessed = pullResponse.TotalItemsProcessed,
113+
TotalNrItems = pullResponse.TotalRequestItems,
114+
QueryId = pullResponse.QueryId
115+
});
116+
108117
if (pullOptions.SaveAfterEveryServiceRequest)
109118
{
110119
_ = await context.SaveChangesAsync(true, false, cancellationToken).ConfigureAwait(false);
@@ -120,10 +129,22 @@ public async Task<PullResult> ExecuteAsync(IEnumerable<PullRequest> requests, Pu
120129
try
121130
{
122131
bool completed = false;
132+
long itemsProcessed = 0;
123133
do
124134
{
125135
Page<object> page = await GetPageAsync(pullRequest.HttpClient, requestUri, pageType, cancellationToken).ConfigureAwait(false);
126-
databaseUpdateQueue.Enqueue(new PullResponse(pullRequest.EntityType, pullRequest.QueryId, page.Items));
136+
itemsProcessed += page.Items.Count();
137+
138+
context.SendSynchronizationEvent(new SynchronizationEventArgs()
139+
{
140+
EventType = SynchronizationEventType.ItemsFetched,
141+
EntityType = pullRequest.EntityType,
142+
ItemsProcessed = itemsProcessed,
143+
TotalNrItems = page.Count ?? 0,
144+
QueryId = pullRequest.QueryId
145+
});
146+
147+
databaseUpdateQueue.Enqueue(new PullResponse(pullRequest.EntityType, pullRequest.QueryId, page.Items, page.Count ?? 0, itemsProcessed));
127148
if (!string.IsNullOrEmpty(page.NextLink))
128149
{
129150
requestUri = new UriBuilder(endpoint) { Query = page.NextLink }.Uri;
@@ -173,6 +194,8 @@ public async Task<PullResult> ExecuteAsync(IEnumerable<PullRequest> requests, Pu
173194
/// <exception cref="DatasyncPullException">Thrown on error</exception>
174195
internal async Task<Page<object>> GetPageAsync(HttpClient client, Uri requestUri, Type pageType, CancellationToken cancellationToken = default)
175196
{
197+
PropertyInfo countPropInfo = pageType.GetProperty("Count")
198+
?? throw new DatasyncException($"Page type '{pageType.Name}' does not have a 'Count' property");
176199
PropertyInfo itemsPropInfo = pageType.GetProperty("Items")
177200
?? throw new DatasyncException($"Page type '{pageType.Name}' does not have an 'Items' property");
178201
PropertyInfo nextLinkPropInfo = pageType.GetProperty("NextLink")
@@ -193,6 +216,7 @@ internal async Task<Page<object>> GetPageAsync(HttpClient client, Uri requestUri
193216

194217
return new Page<object>()
195218
{
219+
Count = (long?)countPropInfo.GetValue(result),
196220
Items = (IEnumerable<object>)itemsPropInfo.GetValue(result)!,
197221
NextLink = (string?)nextLinkPropInfo.GetValue(result)
198222
};
@@ -237,6 +261,8 @@ internal static QueryDescription PrepareQueryDescription(QueryDescription source
237261
/// <param name="EntityType">The type of entity contained within the items.</param>
238262
/// <param name="QueryId">The query ID for the request.</param>
239263
/// <param name="Items">The list of items to process.</param>
264+
/// <param name="TotalRequestItems">The total number of items in the current pull request.</param>
265+
/// <param name="TotalItemsProcessed">The total number of items processed, <paramref name="Items"/> included.</param>
240266
[ExcludeFromCodeCoverage]
241-
internal record PullResponse(Type EntityType, string QueryId, IEnumerable<object> Items);
267+
internal record PullResponse(Type EntityType, string QueryId, IEnumerable<object> Items, long TotalRequestItems, long TotalItemsProcessed);
242268
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
// See the LICENSE file in the project root for more information.
4+
5+
namespace CommunityToolkit.Datasync.Client.Offline;
6+
7+
/// <summary>
8+
/// The list of synchronization events that we support.
9+
/// </summary>
10+
public enum SynchronizationEventType
11+
{
12+
/// <summary>
13+
/// Occurs when items have been successfully fetches from the server.
14+
/// </summary>
15+
/// <remarks>This event is raised after a page of entities was succesfully fetched from the server, ready to be commited to the data store.</remarks>
16+
ItemsFetched,
17+
18+
/// <summary>
19+
/// Occurs when items have been successfully committed to the underlying data store.
20+
/// </summary>
21+
/// <remarks>This event is raised after a page of entities was succesfully commited to the database</remarks>
22+
ItemsCommitted,
23+
}
24+
25+
/// <summary>
26+
/// The event arguments sent when a synchronization event occurs.
27+
/// </summary>
28+
public class SynchronizationEventArgs
29+
{
30+
/// <summary>
31+
/// The type of event.
32+
/// </summary>
33+
public required SynchronizationEventType EventType { get; init; }
34+
35+
/// <summary>
36+
/// The EntityType that is being processed.
37+
/// </summary>
38+
public required Type EntityType { get; init; }
39+
40+
/// <summary>
41+
/// When pulling records, the number of items that have been processed in the current pull request.
42+
/// </summary>
43+
public long ItemsProcessed { get; init; } = -1;
44+
45+
/// <summary>
46+
/// The total number of items in the current pull request.
47+
/// </summary>
48+
public long TotalNrItems { get; init; }
49+
50+
/// <summary>
51+
/// The query ID that is being processed
52+
/// </summary>
53+
public required string QueryId { get; init; }
54+
}

0 commit comments

Comments
 (0)