Skip to content
This repository has been archived by the owner on Feb 29, 2020. It is now read-only.

Commit

Permalink
Merge pull request #432 from hasankhan/multipush
Browse files Browse the repository at this point in the history
[client][managed][offline] Allow pushing selective tables in order
  • Loading branch information
hasankhan committed Oct 9, 2014
2 parents a566f0a + 4fb8226 commit 62e33af
Show file tree
Hide file tree
Showing 15 changed files with 375 additions and 42 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// ----------------------------------------------------------------------------

using System;
using System.Threading;
using System.Threading.Tasks;

Expand All @@ -12,7 +11,7 @@ namespace Microsoft.WindowsAzure.MobileServices.Sync
/// Provides a way to synchronize local database with remote database.
/// </summary>
public interface IMobileServiceSyncContext
{
{
/// <summary>
/// An instance of <see cref="IMobileServiceLocalStore"/>
/// </summary>
Expand All @@ -39,14 +38,14 @@ public interface IMobileServiceSyncContext
/// <summary>
/// Returns the number of pending operations that are not yet pushed to remote table.
/// </summary>
/// <returns>A task that returns the number of pending operations against the remote table.</returns>
/// <returns>Returns the number of pending operations against the remote table.</returns>
long PendingOperations { get; }

/// <summary>
/// Pushes all pending operations up to the remote table.
/// </summary>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe</param>
/// <returns></returns>
/// <returns>A task that completes when pull operation has finished.</returns>
Task PushAsync(CancellationToken cancellationToken);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public interface IMobileServiceSyncTable<T> : IMobileServiceSyncTable
Task DeleteAsync(T instance);

/// <summary>
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync when using the JavaScript Mobile Services backend. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// </summary>
/// <param name="queryKey">
/// A string that uniquely identifies this query and is used to keep track of its sync state. Supplying this parameter enables incremental sync whenever the same key is used again.
Expand All @@ -106,6 +106,26 @@ public interface IMobileServiceSyncTable<T> : IMobileServiceSyncTable
/// </returns>
Task PullAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, CancellationToken cancellationToken);

/// <summary>
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// </summary>
/// <param name="queryKey">
/// A string that uniquely identifies this query and is used to keep track of its sync state. Supplying this parameter enables incremental sync whenever the same key is used again.
/// </param>
/// <param name="query">
/// An OData query that determines which items to
/// pull from the remote table.
/// </param>
/// <param name="pushOtherTables">
/// Push other tables if this table is dirty
/// </param>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
/// </param>
/// <returns>
/// A task that completes when pull operation has finished.
/// </returns>
Task PullAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, bool pushOtherTables, CancellationToken cancellationToken);

/// <summary>
/// Deletes all the items in local table that match the query.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public interface IMobileServiceSyncTable
Task<JObject> LookupAsync(string id);

/// <summary>
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync when using the JavaScript Mobile Services backend. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// </summary>
/// <param name="queryKey">
/// A string that uniquely identifies this query and is used to keep track of its sync state. Supplying this parameter enables incremental sync whenever the same key is used again.
Expand All @@ -106,6 +106,31 @@ public interface IMobileServiceSyncTable
/// </returns>
Task PullAsync(string queryKey, string query, IDictionary<string, string> parameters, CancellationToken cancellationToken);

/// <summary>
/// Pulls all items that match the given query from the associated remote table. Supports incremental sync. For more information, see http://go.microsoft.com/fwlink/?LinkId=506788.
/// </summary>
/// </summary>
/// <param name="queryKey">
/// A string that uniquely identifies this query and is used to keep track of its sync state. Supplying this parameter enables incremental sync whenever the same key is used again.
/// </param>
/// <param name="query">
/// An OData query that determines which items to
/// pull from the remote table.
/// </param>
/// <param name="parameters">
/// A dictionary of user-defined parameters and values to include in
/// the request URI query string.
/// </param>
/// <param name="pushOtherTables">
/// Push other tables if this table is dirty.
/// </param>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
/// </param>
/// <returns>
/// A task that completes when pull operation has finished.
/// </returns>
Task PullAsync(string queryKey, string query, IDictionary<string, string> parameters, bool pushOtherTables, CancellationToken cancellationToken);

/// <summary>
/// Deletes all the items in local table that match the query.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,26 @@ public async Task<JObject> LookupAsync(string tableName, string id)
return await this.Store.LookupAsync(tableName, id);
}

public async Task PullAsync(string tableName, string queryKey, string query, MobileServiceRemoteTableOptions options, IDictionary<string, string> parameters, CancellationToken cancellationToken)
/// <summary>
/// Pulls all items that match the given query from the associated remote table.
/// </summary>
/// <param name="tableName">The name of table to pull</param>
/// <param name="queryKey">A string that uniquely identifies this query and is used to keep track of its sync state.</param>
/// <param name="query">An OData query that determines which items to
/// pull from the remote table.</param>
/// <param name="options">An instance of <see cref="MobileServiceRemoteTableOptions"/></param>
/// <param name="parameters">A dictionary of user-defined parameters and values to include in
/// the request URI query string.</param>
/// <param name="relatedTables">
/// List of tables that may have related records that need to be push before this table is pulled down.
/// When no table is specified, all tables are considered related.
/// </param>
/// <param name="cancellationToken">The <see cref="System.Threading.CancellationToken"/> token to observe
/// </param>
/// <returns>
/// A task that completes when pull operation has finished.
/// </returns>
public async Task PullAsync(string tableName, string queryKey, string query, MobileServiceRemoteTableOptions options, IDictionary<string, string> parameters, IEnumerable<string> relatedTables, CancellationToken cancellationToken)
{
await this.EnsureInitializedAsync();

Expand Down Expand Up @@ -202,7 +221,7 @@ public async Task PullAsync(string tableName, string queryKey, string query, Mob
// let us not burden the server to calculate the count when we don't need it for pull
queryDescription.IncludeTotalCount = false;

var pull = new PullAction(table, this, queryKey, queryDescription, parameters, this.opQueue, this.settings, this.Store, options, cancellationToken);
var pull = new PullAction(table, this, queryKey, queryDescription, parameters, relatedTables, this.opQueue, this.settings, this.Store, options, cancellationToken);
Task discard = this.syncQueue.Post(pull.ExecuteAsync, cancellationToken);

await pull.CompletionTask;
Expand All @@ -220,12 +239,18 @@ public async Task PurgeAsync(string tableName, string queryKey, string query, Ca
await purge.CompletionTask;
}

public async Task PushAsync(CancellationToken cancellationToken)
public Task PushAsync(CancellationToken cancellationToken)
{
return PushAsync(cancellationToken, new string[0]);
}

public async Task PushAsync(CancellationToken cancellationToken, params string[] tableNames)
{
await this.EnsureInitializedAsync();

var push = new PushAction(this.opQueue,
this.Store,
tableNames,
this.Handler,
this.client,
this,
Expand Down Expand Up @@ -269,9 +294,23 @@ public Task CancelAndDiscardItemAsync(MobileServiceTableOperationError error)

public async Task DeferTableActionAsync(TableAction action)
{
IEnumerable<string> tableNames;
if (action.RelatedTables == null) // no related table
{
tableNames = new[] { action.Table.TableName };
}
else if (action.RelatedTables.Any()) // some related tables
{
tableNames = new[] { action.Table.TableName }.Concat(action.RelatedTables);
}
else // all tables are related
{
tableNames = Enumerable.Empty<string>();
}

try
{
await this.PushAsync(action.CancellationToken);
await this.PushAsync(action.CancellationToken, tableNames.ToArray());
}
finally
{
Expand Down Expand Up @@ -304,7 +343,7 @@ private Task ExecuteOperationAsync(MobileServiceTableOperation operation, JObjec
{
return this.ExecuteOperationSafeAsync(operation.ItemId, operation.TableName, async () =>
{
MobileServiceTableOperation existing = await this.opQueue.GetOperationByItemIdAsync(operation.ItemId);
MobileServiceTableOperation existing = await this.opQueue.GetOperationByItemIdAsync(operation.TableName, operation.ItemId);
if (existing != null)
{
existing.Validate(operation); // make sure this operation is legal and collapses after any previous operation on same item already in the queue
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@
using System.Globalization;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.WindowsAzure.MobileServices.Query;
using Newtonsoft.Json.Linq;

namespace Microsoft.WindowsAzure.MobileServices.Sync
{
internal class MobileServiceSyncTable<T>: MobileServiceSyncTable, IMobileServiceSyncTable<T>
internal class MobileServiceSyncTable<T> : MobileServiceSyncTable, IMobileServiceSyncTable<T>
{
private MobileServiceTableQueryProvider queryProvider;
private IMobileServiceTable<T> remoteTable;

public MobileServiceSyncTable(string tableName, MobileServiceClient client): base(tableName, client)
public MobileServiceSyncTable(string tableName, MobileServiceClient client)
: base(tableName, client)
{
this.remoteTable = client.GetTable<T>();
this.queryProvider = new MobileServiceTableQueryProvider(this);
Expand All @@ -44,14 +44,30 @@ public Task<IEnumerable<U>> ReadAsync<U>(IMobileServiceTableQuery<U> query)
}

public Task PullAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, CancellationToken cancellationToken)
{
return PullAsync(queryKey, query, cancellationToken, new string[0]);
}

public Task PullAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, CancellationToken cancellationToken, params string[] tableNames)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
string queryString = this.queryProvider.ToODataString(query);

return this.PullAsync(queryKey, queryString, query.Parameters, cancellationToken, tableNames);
}

public Task PullAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, bool pushOtherTables, CancellationToken cancellationToken)
{
if (query == null)
{
throw new ArgumentNullException("query");
}
string queryString = this.queryProvider.ToODataString(query);

return this.PullAsync(queryKey, queryString, query.Parameters, cancellationToken);
return this.PullAsync(queryKey, queryString, query.Parameters, pushOtherTables, cancellationToken);
}

public Task PurgeAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, CancellationToken cancellationToken)
Expand All @@ -64,7 +80,7 @@ public Task PurgeAsync<U>(string queryKey, IMobileServiceTableQuery<U> query, Ca

return this.PurgeAsync(queryKey, queryString, cancellationToken);
}

public async Task RefreshAsync(T instance)
{
if (instance == null)
Expand All @@ -79,7 +95,7 @@ public async Task RefreshAsync(T instance)
return; // refresh is not supposed to throw if your object does not have an id for some reason
}

string id = EnsureIdIsString(objId);
string id = EnsureIdIsString(objId);

// Get the latest version of this element
JObject refreshed = await base.LookupAsync(id);
Expand All @@ -91,7 +107,7 @@ public async Task RefreshAsync(T instance)
CultureInfo.InvariantCulture,
Resources.MobileServiceTable_ItemNotFoundInStore));
}

// Deserialize that value back into the current instance
serializer.Deserialize<T>(refreshed, instance);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,20 @@ public Task<JToken> ReadAsync(string query)
}

public Task PullAsync(string queryKey, string query, IDictionary<string, string> parameters, CancellationToken cancellationToken)
{
return PullAsync(queryKey, query, parameters, cancellationToken, new string[0]);
}

public Task PullAsync(string queryKey, string query, IDictionary<string, string> parameters, CancellationToken cancellationToken, params string[] tableNames)
{
ValidateQueryKey(queryKey);
return this.syncContext.PullAsync(this.TableName, queryKey, query, this.SupportedOptions, parameters, tableNames ?? new string[0], cancellationToken);
}

public Task PullAsync(string queryKey, string query, IDictionary<string, string> parameters, bool pushOtherTables, CancellationToken cancellationToken)
{
ValidateQueryKey(queryKey);
return this.syncContext.PullAsync(this.TableName, queryKey, query, this.SupportedOptions, parameters, cancellationToken);
return this.syncContext.PullAsync(this.TableName, queryKey, query, this.SupportedOptions, parameters, pushOtherTables ? new string[0] : null, cancellationToken);
}

public Task PurgeAsync(string queryKey, string query, CancellationToken cancellationToken)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ public PullAction(MobileServiceTable table,
string queryKey,
MobileServiceTableQueryDescription query,
IDictionary<string, string> parameters,
IEnumerable<string> relatedTables,
OperationQueue operationQueue,
MobileServiceSyncSettingsManager settings,
IMobileServiceLocalStore store,
MobileServiceRemoteTableOptions options,
CancellationToken cancellationToken)
: base(table, queryKey, query, context, operationQueue, settings, store, cancellationToken)
: base(table, queryKey, query, relatedTables, context, operationQueue, settings, store, cancellationToken)
{
this.options = options;
this.parameters = parameters;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public PurgeAction(MobileServiceTable table,
MobileServiceSyncSettingsManager settings,
IMobileServiceLocalStore store,
CancellationToken cancellationToken)
: base(table, queryKey, query, context, operationQueue, settings, store, cancellationToken)
: base(table, queryKey, query, null, context, operationQueue, settings, store, cancellationToken)
{
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,18 @@ internal class PushAction : SyncAction
private IMobileServiceSyncHandler syncHandler;
private MobileServiceClient client;
private MobileServiceSyncContext context;
private IEnumerable<string> tableNames;

public PushAction(OperationQueue operationQueue,
IMobileServiceLocalStore store,
IEnumerable<string> tableNames,
IMobileServiceSyncHandler syncHandler,
MobileServiceClient client,
MobileServiceSyncContext context,
CancellationToken cancellationToken)
: base(operationQueue, store, cancellationToken)
{
this.tableNames = tableNames;
this.client = client;
this.syncHandler = syncHandler;
this.context = context;
Expand Down Expand Up @@ -121,7 +124,7 @@ private async Task FinalizePush(OperationBatch batch, MobileServicePushStatus ba

private async Task ExecuteAllOperationsAsync(OperationBatch batch)
{
MobileServiceTableOperation operation = await this.OperationQueue.PeekAsync(0);
MobileServiceTableOperation operation = await this.OperationQueue.PeekAsync(0, this.tableNames);

// keep taking out operations and executing them until queue is empty or operation finds the bookmark or batch is aborted
while (operation != null)
Expand All @@ -140,7 +143,7 @@ private async Task ExecuteAllOperationsAsync(OperationBatch batch)
}

// get next operation
operation = await this.OperationQueue.PeekAsync(operation.Sequence);
operation = await this.OperationQueue.PeekAsync(operation.Sequence, this.tableNames);
}
}

Expand Down
Loading

0 comments on commit 62e33af

Please sign in to comment.