protected async override Task<bool> HandleDirtyTable() { if (this.Query.Filter != null || !this.force) { throw new InvalidOperationException("The table cannot be purged because it has pending operations."); } var delOperationsQuery = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.OperationQueue); delOperationsQuery.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, "tableName"), new ConstantNode(this.Table.TableName)); // count ops to be deleted delOperationsQuery.IncludeTotalCount = true; delOperationsQuery.Top = 0; long toRemove = QueryResult.Parse(await this.Store.ReadAsync(delOperationsQuery), null, validate: false).TotalCount; // delete operations delOperationsQuery.Top = null; await this.Store.DeleteAsync(delOperationsQuery); // delete errors var delErrorsQuery = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.SyncErrors); delErrorsQuery.Filter = delOperationsQuery.Filter; await this.Store.DeleteAsync(delErrorsQuery); // update queue operation count this.OperationQueue.UpdateOperationCount(-toRemove); return true; }
public Task<JToken> ReadAsync(MobileServiceTableQueryDescription query) { if (query.TableName == MobileServiceLocalSystemTables.OperationQueue || query.TableName == MobileServiceLocalSystemTables.SyncErrors) { MockTable table = GetTable(query.TableName); IEnumerable<JObject> items = table.Values; if (query.TableName == MobileServiceLocalSystemTables.OperationQueue) { string odata = query.ToODataString(); if (odata.Contains("$orderby=sequence desc")) // the query to take total count and max sequence { items = items.OrderBy(o => o.Value<long>("sequence")); } else if (odata.StartsWith("$filter=((tableKind eq ") && odata.Contains("(sequence gt ")) { var sequenceCompareNode = ((BinaryOperatorNode)query.Filter).RightOperand as BinaryOperatorNode; items = items.Where(o => o.Value<long>("sequence") > (long)((ConstantNode)sequenceCompareNode.RightOperand).Value); items = items.OrderBy(o => o.Value<long>("sequence")); } else if (odata.Contains("(sequence gt ")) // the query to get next operation { items = items.Where(o => o.Value<long>("sequence") > (long)((ConstantNode)((BinaryOperatorNode)query.Filter).RightOperand).Value); items = items.OrderBy(o => o.Value<long>("sequence")); } else if (odata.Contains(") and (itemId eq '")) // the query to retrive operation by item id { string targetTable = ((ConstantNode)((BinaryOperatorNode)((BinaryOperatorNode)query.Filter).LeftOperand).RightOperand).Value.ToString(); string targetId = ((ConstantNode)((BinaryOperatorNode)((BinaryOperatorNode)query.Filter).RightOperand).RightOperand).Value.ToString(); items = items.Where(o => o.Value<string>("itemId") == targetId && o.Value<string>("tableName") == targetTable); } else if (odata.Contains("$filter=(tableName eq '")) { items = items.Where(o => o.Value<string>("tableName") == ((ConstantNode)((BinaryOperatorNode)query.Filter).RightOperand).Value.ToString()); } } if (query.IncludeTotalCount) { return Task.FromResult<JToken>(new JObject() { { "count", items.Count() }, { "results", new JArray(items) } }); } return Task.FromResult<JToken>(new JArray(items)); } this.ReadQueries.Add(query); JToken response; if (ReadAsyncFunc != null) { response = ReadAsyncFunc(query); } else { response = JToken.Parse(ReadResponses.Dequeue()); } return Task.FromResult(response); }
private async Task Delete(MobileServiceTableQueryDescription query) { await this.store.DeleteAsync(query); OperationsInfo operationsInfo = this.operationsInfo.Value; Interlocked.Decrement(ref operationsInfo.Count); }
public async Task DoesNotUpsertAnObject_IfItDoesNotHaveAnId() { var query = new MobileServiceTableQueryDescription("test"); var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, null, query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, null, CancellationToken.None); var itemWithId = new JObject() { { "id", "abc" }, { "text", "has id" } }; var itemWithoutId = new JObject() { { "text", "no id" } }; var result = new JArray(new[]{ itemWithId, itemWithoutId }); this.opQueue.Setup(q => q.LockTableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<IDisposable>(null)); this.opQueue.Setup(q => q.CountPending(It.IsAny<string>())).Returns(Task.FromResult(0L)); this.table.SetupSequence(t => t.ReadAsync(It.IsAny<string>(), It.IsAny<IDictionary<string, string>>(), It.IsAny<MobileServiceFeatures>())) .Returns(Task.FromResult(QueryResult.Parse(result, null, false))) .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false))); this.store.Setup(s => s.UpsertAsync("test", It.IsAny<IEnumerable<JObject>>(), true)) .Returns(Task.FromResult(0)) .Callback<string, IEnumerable<JObject>, bool>((tableName, items, fromServer) => { Assert.AreEqual(1, items.Count()); Assert.AreEqual(itemWithId, items.First()); }); await action.ExecuteAsync(); store.VerifyAll(); opQueue.VerifyAll(); table.VerifyAll(); store.Verify(s => s.DeleteAsync("test", It.IsAny<IEnumerable<string>>()), Times.Never(), "There shouldn't be any call to delete"); }
/// <summary> /// Execute a query and return its results. /// </summary> /// <typeparam name="T"> /// The type of element returned by the query. /// </typeparam> /// <param name="query"> /// The query to evaluate and get the results for. /// </param> /// <returns> /// Results of the query. /// </returns> internal async Task <IEnumerable <T> > Execute <T>(IMobileServiceTableQuery <T> query) { // Compile the query from the underlying IQueryable's expression // tree MobileServiceTableQueryDescription compiledQuery = this.Compile(query); // Send the query string odata = compiledQuery.ToODataString(); QueryResult result = await this.Execute <T>(query, odata); return(new QueryResultEnumerable <T>( result.TotalCount, result.NextLink, query.Table.MobileServiceClient.Serializer.Deserialize(result.Values, compiledQuery.ProjectionArgumentType).Select( value => { // Apply the projection to the instance transforming it // as desired foreach (Delegate projection in compiledQuery.Projections) { value = projection.DynamicInvoke(value); } return (T)value; }))); }
/// <summary> /// Counts all the items returned from the query /// </summary> /// <param name="store">An instance of <see cref="IMobileServiceLocalStore"/></param> /// <param name="query">An instance of <see cref="MobileServiceTableQueryDescription"/></param> /// <returns>Task that will complete with count of items.</returns> public static async Task<long> CountAsync(this IMobileServiceLocalStore store, MobileServiceTableQueryDescription query) { query.Top = 0; query.IncludeTotalCount = true; QueryResult result = await store.QueryAsync(query); return result.TotalCount; }
public PullStrategy(MobileServiceTableQueryDescription query, PullCursor cursor, MobileServiceRemoteTableOptions options) { this.Query = query; this.Cursor = cursor; this.SupportsSkip = options.HasFlag(MobileServiceRemoteTableOptions.Skip); this.SupportsTop = options.HasFlag(MobileServiceRemoteTableOptions.Top); }
/// <summary> /// Compile the query into a MobileServiceTableQueryDescription. /// </summary> /// <returns> /// The compiled OData query. /// </returns> internal MobileServiceTableQueryDescription Compile <T>(IMobileServiceTableQuery <T> query) { // Compile the query from the underlying IQueryable's expression // tree MobileServiceTableQueryTranslator <T> translator = new MobileServiceTableQueryTranslator <T>(query); MobileServiceTableQueryDescription compiledQuery = translator.Translate(); return(compiledQuery); }
/// <summary> /// Initializes a new instance of the MobileServiceTableQueryTranslator /// class. /// </summary> /// <param name="query"> /// The <see cref="T:MobileServiceTableQuery`1{T}"/> which /// is being translated. /// </param> internal MobileServiceTableQueryTranslator(IMobileServiceTableQuery <T> query) { Arguments.IsNotNull(query, nameof(query)); this.query = query; this.queryDescription = new MobileServiceTableQueryDescription(query.Table.TableName) { IncludeTotalCount = query.RequestTotalCount, }; }
/// <summary> /// Initializes a new instance of the MobileServiceTableQueryTranslator /// class. /// </summary> /// <param name="query"> /// The <see cref="T:MobileServiceTableQuery`1{T}"/> which /// is being translated. /// </param> internal MobileServiceTableQueryTranslator(IMobileServiceTableQuery <T> query) { Debug.Assert(query != null); this.query = query; this.queryDescription = new MobileServiceTableQueryDescription(query.Table.TableName) { IncludeTotalCount = query.RequestTotalCount, }; }
private OperationsInfo LoadOperationsInformation() { var query = new MobileServiceTableQueryDescription(FileOperationTableName); query.IncludeTotalCount = true; query.Top = 1; // Get the last item in the queue query.Ordering.Add(new OrderByNode(new MemberAccessNode(null, "sequence"), OrderByDirection.Descending)); JToken result = this.store.ReadAsync(query).Result; return new OperationsInfo(result.Value<int>(CountPropertyName), result.Value<int>("sequence")); }
public async Task SavesTheMaxUpdatedAt_IfQueryIdIsSpecified_WithoutFilter() { var query = new MobileServiceTableQueryDescription("test"); var result = new JArray(new[] { new JObject() { { "id", "abc" }, { "text", "has id"}, { "__updatedAt", "1985-07-17" } }, new JObject() { { "id", "abc" }, { "text", "has id"}, { "__updatedAt", "2014-07-09" } } }); string firstQuery = "$filter=(__updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00')&$orderby=__updatedAt&$skip=0&$top=50"; string secondQuery = "$filter=(__updatedAt ge datetimeoffset'2014-07-09T07%3A00%3A00.0000000%2B00%3A00')&$orderby=__updatedAt&$skip=0&$top=50"; await TestIncrementalSync(query, result, new DateTime(2014, 07, 09), savesMax: true, firstQuery: firstQuery, secondQuery: secondQuery); }
public PurgeAction(MobileServiceTable table, MobileServiceTableKind tableKind, string queryId, MobileServiceTableQueryDescription query, bool force, MobileServiceSyncContext context, OperationQueue operationQueue, MobileServiceSyncSettingsManager settings, IMobileServiceLocalStore store, CancellationToken cancellationToken) : base(table, tableKind, queryId, query, null, context, operationQueue, settings, store, cancellationToken) { this.force = force; }
public IncrementalPullStrategy(MobileServiceTable table, MobileServiceTableQueryDescription query, string queryId, MobileServiceSyncSettingsManager settings, PullCursor cursor, MobileServiceRemoteTableOptions options) : base(query, cursor, options) { this.table = table; this.originalFilter = query.Filter; this.queryId = queryId; this.settings = settings; this.ordered = options.HasFlag(MobileServiceRemoteTableOptions.OrderBy); }
/// <summary> /// Creates a copy of <see cref="MobileServiceTableQueryDescription"/> /// </summary> /// <returns>The cloned query</returns> public MobileServiceTableQueryDescription Clone() { var clone = new MobileServiceTableQueryDescription(this.TableName); clone.Filter = this.Filter; clone.Selection = this.Selection.ToList(); clone.Ordering = this.Ordering.ToList(); clone.Projections = this.Projections.ToList(); clone.ProjectionArgumentType = this.ProjectionArgumentType; clone.Skip = this.Skip; clone.Top = this.Top; clone.IncludeTotalCount = this.IncludeTotalCount; return(clone); }
private async Task<IMobileServiceFileOperation> GetNextOperationItemAsync(bool deleteItem) { var query = new MobileServiceTableQueryDescription(FileOperationTableName); query.Ordering.Add(new OrderByNode(new MemberAccessNode(null, "sequence"), OrderByDirection.Ascending)); query.Top = 1; JToken result = await this.store.ReadAsync(query); FileOperationItem operationItem = result.ToObject<List<FileOperationItem>>().FirstOrDefault(); if (deleteItem) { await Delete(query); } return operationItem != null ? operationItem.ToOperation() : null; }
public TableAction(MobileServiceTable table, MobileServiceTableKind tableKind, string queryId, MobileServiceTableQueryDescription query, IEnumerable<string> relatedTables, MobileServiceSyncContext context, OperationQueue operationQueue, MobileServiceSyncSettingsManager settings, IMobileServiceLocalStore store, CancellationToken cancellationToken) : base(operationQueue, store, cancellationToken) { this.Table = table; this.TableKind = tableKind; this.QueryId = queryId; this.Query = query; this.RelatedTables = relatedTables; this.Settings = settings; this.Context = context; }
public PullAction(MobileServiceTable table, MobileServiceTableKind tableKind, MobileServiceSyncContext context, string queryId, MobileServiceTableQueryDescription query, IDictionary<string, string> parameters, IEnumerable<string> relatedTables, OperationQueue operationQueue, MobileServiceSyncSettingsManager settings, IMobileServiceLocalStore store, MobileServiceRemoteTableOptions options, MobileServiceObjectReader reader, CancellationToken cancellationToken) : base(table, tableKind, queryId, query, relatedTables, context, operationQueue, settings, store, cancellationToken) { this.options = options; this.parameters = parameters; this.cursor = new PullCursor(query); this.Reader = reader ?? new MobileServiceObjectReader(); }
public async Task RemoveAsync(string id) { var query = new MobileServiceTableQueryDescription(FileOperationTableName); query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, MobileServiceSystemColumns.Id), new ConstantNode(id)); query.Top = 1; await Delete(query); }
public async Task DeleteAsync_WithQuery_SendsNotification() { var store = new MobileServiceLocalStoreMock(); var trackingContext = new StoreTrackingContext(StoreOperationSource.Local, string.Empty); var eventManager = new MobileServiceEventManagerMock<IMobileServiceEvent>(); var settings = new MobileServiceSyncSettingsManager(store); var changeTracker = new LocalStoreChangeTracker(store, trackingContext, eventManager, settings); JObject item = EnqueueSimpleObjectResponse(store); StoreOperationCompletedEvent operationEvent = null; eventManager.PublishAsyncFunc = t => { operationEvent = t as StoreOperationCompletedEvent; return Task.FromResult(0); }; MobileServiceTableQueryDescription query = new MobileServiceTableQueryDescription("test"); query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, MobileServiceSystemColumns.Id), new ConstantNode("123")); await changeTracker.DeleteAsync(query); Assert.IsNotNull(operationEvent); Assert.AreEqual(operationEvent.Operation.Kind, LocalStoreOperationKind.Delete); Assert.AreEqual(operationEvent.Operation.RecordId, "123"); Assert.AreEqual(operationEvent.Operation.TableName, "test"); }
/// <summary> /// Counts all the items in a local table /// </summary> /// <param name="store">Instance of <see cref="IMobileServiceLocalStore"/></param> /// <param name="tableName">Name of the table</param> /// <returns>Task that will complete with count of items.</returns> public async static Task<long> CountAsync(this IMobileServiceLocalStore store, string tableName) { var query = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.OperationQueue); return await CountAsync(store, query); }
public void ToODataString_EscapesThe_Uri() { //updatedat gt datetimeoffset'2014-04-04T07:00:00.0000000+00:00' var datetime1 = new ConstantNode(new DateTimeOffset(2014, 4, 4, 7, 0, 0, TimeSpan.FromHours(0))); var updatedAt = new MemberAccessNode(null, "updatedat"); var gt1 = new BinaryOperatorNode(BinaryOperatorKind.GreaterThan, updatedAt, datetime1); // updatedat gt datetime'2014-04-04T07:0:0.000Z' var datetime2 = new ConstantNode(new DateTime(2014, 4, 4, 7, 0, 0, DateTimeKind.Utc)); var someDate = new MemberAccessNode(null, "someDate"); var gt2 = new BinaryOperatorNode(BinaryOperatorKind.GreaterThan, someDate, datetime2); // startswith(text,'this&''%%=,?#') var text = new MemberAccessNode(null, "text"); var value = new ConstantNode("this&'%%=,?#"); var startswith = new FunctionCallNode("startswith", new QueryNode[] { text, value }); //updatedat gt datetimeoffset'2014-04-04T07:00:00.0000000+00:00' and startswith(text,'this&''%%=,?#') var and2 = new BinaryOperatorNode(BinaryOperatorKind.And, gt2, startswith); var and1 = new BinaryOperatorNode(BinaryOperatorKind.And, gt1, and2); var desc = new MobileServiceTableQueryDescription("someTable") { Filter = and1 }; Assert.AreEqual(desc.ToODataString(), EscapedODataString); }
public async Task UpsertAsync_ThenReadAsync_AllTypes() { TestUtilities.DropTestTable(TestDbName, TestTable); // first create a table called todo using (MobileServiceSQLiteStore store = new MobileServiceSQLiteStore(TestDbName)) { store.DefineTable(TestTable, JObjectTypes.GetObjectWithAllTypes()); await store.InitializeAsync(); var upserted = new JObject() { { "id", "xyz" }, { "Object", new JObject() { {"id", "abc"} }}, { "Array", new JArray() { new JObject(){{"id", 3}} } }, { "Integer", 123L }, { "Float", 12.5m }, { "String", "def" }, { "Boolean", true }, { "Date", new DateTime(2003, 5, 6, 4, 5, 1, DateTimeKind.Utc) }, { "Bytes", new byte[] { 1, 2, 3} }, { "Guid", new Guid("AB3EB1AB-53CD-4780-928B-A7E1CB7A927C") }, { "TimeSpan", new TimeSpan(1234) } }; await store.UpsertAsync(TestTable, new[] { upserted }, false); var query = new MobileServiceTableQueryDescription(TestTable); var items = await store.ReadAsync(query) as JArray; Assert.IsNotNull(items); Assert.AreEqual(items.Count, 1); var lookedup = items.First as JObject; Assert.AreEqual(upserted.ToString(Formatting.None), lookedup.ToString(Formatting.None)); } }
public async Task<IMobileServiceFileOperation> GetOperationByFileIdAsync(string fileId) { var query = new MobileServiceTableQueryDescription(FileOperationTableName); query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new MemberAccessNode(null, "fileId"), new ConstantNode(fileId)); query.Top = 1; JToken result = await this.store.ReadAsync(query); FileOperationItem operationItem = result.ToObject<List<FileOperationItem>>().FirstOrDefault(); return operationItem != null ? operationItem.ToOperation() : null; }
public async Task DoesNotSaveTheMaxUpdatedAt_IfThereAreNoResults() { var query = new MobileServiceTableQueryDescription("test"); var result = new JArray(); string expectedOdata = "$filter=(__updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00')&$orderby=__updatedAt&$skip=0&$top=50"; await TestIncrementalSync(query, result, DateTime.MinValue, savesMax: false, firstQuery: expectedOdata, secondQuery: null); }
public async Task DoesNotSaveTheMaxUpdatedAt_IfResultsDoNotHaveUpdatedAt() { var query = new MobileServiceTableQueryDescription("test"); query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new ConstantNode(4), new ConstantNode(3)); var result = new JArray(new[] { new JObject() { { "id", "abc" }, { "text", "has id"} }, new JObject() { { "id", "abc" }, { "text", "has id"} } }); string firstQuery = "$filter=((4 eq 3) and (__updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00'))&$orderby=__updatedAt&$skip=0&$top=50"; string secondQuery = "$filter=((4 eq 3) and (__updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00'))&$orderby=__updatedAt&$skip=2&$top=50"; await TestIncrementalSync(query, result, new DateTime(2014, 07, 09), savesMax: false, firstQuery: firstQuery, secondQuery: secondQuery); }
internal string ToODataString <T>(IMobileServiceTableQuery <T> query) { MobileServiceTableQueryDescription description = this.Compile(query); return(description.ToODataString()); }
public async Task SavesTheMaxUpdatedAt_IfQueryIdIsSpecified() { var query = new MobileServiceTableQueryDescription("test"); query.Filter = new BinaryOperatorNode(BinaryOperatorKind.Equal, new ConstantNode(4), new ConstantNode(3)); query.Ordering.Add(new OrderByNode(new MemberAccessNode(null, "text"), OrderByDirection.Descending)); var result = new JArray(new[] { new JObject() { { "id", "abc" }, { "text", "has id"}, { "__updatedAt", "1985-07-17" } }, new JObject() { { "id", "abc" }, { "text", "has id"}, { "__updatedAt", "2014-07-09" } } }); string firstQuery = "$filter=((4 eq 3) and (__updatedAt ge datetimeoffset'2013-01-01T00%3A00%3A00.0000000%2B00%3A00'))&$orderby=__updatedAt&$skip=0&$top=50"; string secondQuery = "$filter=((4 eq 3) and (__updatedAt ge datetimeoffset'2014-07-09T07%3A00%3A00.0000000%2B00%3A00'))&$orderby=__updatedAt&$skip=0&$top=50"; await TestIncrementalSync(query, result, new DateTime(2014, 07, 09), savesMax: true, firstQuery: firstQuery, secondQuery: secondQuery); }
/// <summary> /// Creates a copy of <see cref="MobileServiceTableQueryDescription"/> /// </summary> /// <returns>The cloned query</returns> public MobileServiceTableQueryDescription Clone() { var clone = new MobileServiceTableQueryDescription(this.TableName); clone.Filter = this.Filter; clone.Selection = this.Selection.ToList(); clone.Ordering = this.Ordering.ToList(); clone.Projections = this.Projections.ToList(); clone.ProjectionArgumentType = this.ProjectionArgumentType; clone.Skip = this.Skip; clone.Top = this.Top; clone.IncludeTotalCount = this.IncludeTotalCount; return clone; }
private static MobileServiceTableQueryDescription Parse(string tableName, string query, string uriPath) { bool includeTotalCount = false; int? top = null; int? skip = null; string[] selection = null; QueryNode filter = null; IList <OrderByNode> orderings = null; IDictionary <string, string> parameters = HttpUtility.ParseQueryString(query); foreach (KeyValuePair <string, string> parameter in parameters) { string key = parameter.Key; string value = parameter.Value; if (String.IsNullOrEmpty(key)) { continue; } switch (key) { case ODataOptions.Filter: filter = ODataExpressionParser.ParseFilter(value); break; case ODataOptions.OrderBy: orderings = ODataExpressionParser.ParseOrderBy(value); break; case ODataOptions.Skip: skip = Int32.Parse(value); break; case ODataOptions.Top: top = Int32.Parse(value); break; case ODataOptions.Select: selection = value.Split(','); break; case ODataOptions.InlineCount: includeTotalCount = "allpages".Equals(value); break; default: throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unrecognized query parameter '{0}'.", key), "query"); } } var description = new MobileServiceTableQueryDescription(tableName) { IncludeTotalCount = includeTotalCount, Skip = skip, Top = top }; description.UriPath = uriPath; if (selection != null) { ((List <string>)description.Selection).AddRange(selection); } if (orderings != null) { ((List <OrderByNode>)description.Ordering).AddRange(orderings); } description.Filter = filter; return(description); }
private static MobileServiceTableQueryDescription CreateQuery() { var query = new MobileServiceTableQueryDescription(MobileServiceLocalSystemTables.OperationQueue); return query; }
/// <summary> /// Deletes items from local table that match the given query. /// </summary> /// <param name="query">A query to find records to delete.</param> /// <returns>A task that completes when delete query has executed.</returns> public override Task DeleteAsync(MobileServiceTableQueryDescription query) { if (query == null) { throw new ArgumentNullException("query"); } this.EnsureInitialized(); var formatter = new SqlQueryFormatter(query); string sql = formatter.FormatDelete(); return this.operationSemaphore.WaitAsync() .ContinueWith(t => { try { this.ExecuteNonQuery(sql, formatter.Parameters); } finally { this.operationSemaphore.Release(); } }); }
/// <summary> /// Reads data from local store by executing the query. /// </summary> /// <param name="query">The query to execute on local store.</param> /// <returns>A task that will return with results when the query finishes.</returns> public override Task<JToken> ReadAsync(MobileServiceTableQueryDescription query) { if (query == null) { throw new ArgumentNullException("query"); } this.EnsureInitialized(); var formatter = new SqlQueryFormatter(query); string sql = formatter.FormatSelect(); return this.operationSemaphore.WaitAsync() .ContinueWith(t => { try { IList<JObject> rows = this.ExecuteQuery(query.TableName, sql, formatter.Parameters); JToken result = new JArray(rows.ToArray()); if (query.IncludeTotalCount) { sql = formatter.FormatSelectCount(); IList<JObject> countRows = null; countRows = this.ExecuteQuery(query.TableName, sql, formatter.Parameters); long count = countRows[0].Value<long>("count"); result = new JObject() { { "results", result }, { "count", count } }; } return result; } finally { this.operationSemaphore.Release(); } }); }
/// <summary> /// Executes the query on local store and returns the parsed result /// </summary> /// <param name="store">An instance of <see cref="IMobileServiceLocalStore"/></param> /// <param name="query">An instance of <see cref="MobileServiceTableQueryDescription"/></param> /// <returns>Task that will complete with the parsed result of the query.</returns> public static async Task<QueryResult> QueryAsync(this IMobileServiceLocalStore store, MobileServiceTableQueryDescription query) { return QueryResult.Parse(await store.ReadAsync(query), null, validate: true); }
private async Task TestIncrementalSync(MobileServiceTableQueryDescription query, JArray result, DateTime maxUpdatedAt, bool savesMax, string firstQuery, string secondQuery) { var action = new PullAction(this.table.Object, MobileServiceTableKind.Table, this.context.Object, "latestItems", query, null, null, this.opQueue.Object, this.settings.Object, this.store.Object, MobileServiceRemoteTableOptions.All, null, CancellationToken.None); this.opQueue.Setup(q => q.LockTableAsync(It.IsAny<string>(), It.IsAny<CancellationToken>())).Returns(Task.FromResult<IDisposable>(null)); this.opQueue.Setup(q => q.CountPending(It.IsAny<string>())).Returns(Task.FromResult(0L)); this.table.Setup(t => t.ReadAsync(firstQuery, It.IsAny<IDictionary<string, string>>(), It.IsAny<MobileServiceFeatures>())) .Returns(Task.FromResult(QueryResult.Parse(result, null, false))); if (result.Any()) { this.table.Setup(t => t.ReadAsync(secondQuery, It.IsAny<IDictionary<string, string>>(), It.IsAny<MobileServiceFeatures>())) .Returns(Task.FromResult(QueryResult.Parse(new JArray(), null, false))); } if (result.Any()) { this.store.Setup(s => s.UpsertAsync("test", It.IsAny<IEnumerable<JObject>>(), true)).Returns(Task.FromResult(0)); } this.settings.Setup(s => s.GetDeltaTokenAsync("test", "latestItems")).Returns(Task.FromResult(new DateTimeOffset(2013, 1, 1, 0, 0, 0, TimeSpan.Zero))); if (savesMax) { this.settings.Setup(s => s.SetDeltaTokenAsync("test", "latestItems", maxUpdatedAt)).Returns(Task.FromResult(0)); } await action.ExecuteAsync(); this.store.VerifyAll(); this.opQueue.VerifyAll(); this.table.VerifyAll(); this.settings.VerifyAll(); store.Verify(s => s.DeleteAsync("test", It.IsAny<IEnumerable<string>>()), Times.Never(), "There shouldn't be any call to delete"); }
private static MobileServiceTableQueryDescription Parse(string tableName, string query, string uriPath) { bool includeTotalCount = false; int? top = null; int? skip = null; string[] selection = null; QueryNode filter = null; IList<OrderByNode> orderings = null; IDictionary<string, string> parameters = HttpUtility.ParseQueryString(query); foreach (KeyValuePair<string, string> parameter in parameters) { string key = parameter.Key; string value = parameter.Value; if (String.IsNullOrEmpty(key)) { continue; } switch (key) { case ODataOptions.Filter: filter = ODataExpressionParser.ParseFilter(value); break; case ODataOptions.OrderBy: orderings = ODataExpressionParser.ParseOrderBy(value); break; case ODataOptions.Skip: skip = Int32.Parse(value); break; case ODataOptions.Top: top = Int32.Parse(value); break; case ODataOptions.Select: selection = value.Split(','); break; case ODataOptions.InlineCount: includeTotalCount = "allpages".Equals(value); break; default: throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, "Unrecognized query parameter '{0}'.", key), "query"); } } var description = new MobileServiceTableQueryDescription(tableName) { IncludeTotalCount = includeTotalCount, Skip = skip, Top = top }; description.UriPath = uriPath; if (selection != null) { ((List<string>)description.Selection).AddRange(selection); } if (orderings != null) { ((List<OrderByNode>)description.Ordering).AddRange(orderings); } description.Filter = filter; return description; }
/// <summary> /// Executes the query on local store and returns the first or default item from parsed result /// </summary> /// <param name="store">An instance of <see cref="IMobileServiceLocalStore"/></param> /// <param name="query">An instance of <see cref="MobileServiceTableQueryDescription"/></param> /// <returns>Task that will complete with the first or default item from parsed result of the query.</returns> public static async Task<JObject> FirstOrDefault(this IMobileServiceLocalStore store, MobileServiceTableQueryDescription query) { QueryResult result = await store.QueryAsync(query); return result.Values.FirstOrDefault() as JObject; }