private void ReportLicensingUsage(string id, ReportHotSpareUssage.ActivationMode mode) { try { var requestParam = new CreateHttpJsonRequestParams(null, "http://licensing.ravendb.net/hot-spare/activation", HttpMethod.Post, null, null, conventions); var request = requestFactory.CreateHttpJsonRequest(requestParam); request.WriteAsync( RavenJObject.FromObject(new ReportHotSpareUssage() { LicenseId = id, Mode = mode })); } catch (Exception e) { log.WarnException("Failed to notify about hot sapre licensing usage.", e); } }
/// <summary> /// Initializes a new instance of the <see cref="AsyncServerClient"/> class. /// </summary> public AsyncServerClient(string url, DocumentConvention convention, ICredentials credentials, HttpJsonRequestFactory jsonRequestFactory, Guid?sessionId) { profilingInformation = ProfilingInformation.CreateProfilingInformation(sessionId); this.url = url.EndsWith("/") ? url.Substring(0, url.Length - 1) : url; this.convention = convention; this.credentials = credentials; this.jsonRequestFactory = jsonRequestFactory; this.sessionId = sessionId; // required to ensure just a single auth dialog Task task = jsonRequestFactory.CreateHttpJsonRequest(this, (url + "/docs?pageSize=0").NoCache(), "GET", credentials, convention) .ExecuteRequest(); jsonRequestFactory.ConfigureRequest += (sender, args) => { args.JsonRequest.WaitForTask = task; }; }
/// <summary> /// Initialize the document store access method to RavenDB /// </summary> protected virtual void InitializeInternal() { #if !SILVERLIGHT && !NETFX_CORE var rootDatabaseUrl = MultiDatabase.GetRootDatabaseUrl(Url); var rootServicePoint = ServicePointManager.FindServicePoint(new Uri(rootDatabaseUrl)); rootServicePoint.UseNagleAlgorithm = false; rootServicePoint.Expect100Continue = false; rootServicePoint.ConnectionLimit = 256; databaseCommandsGenerator = () => { string databaseUrl = Url; if (string.IsNullOrEmpty(DefaultDatabase) == false) { databaseUrl = rootDatabaseUrl; databaseUrl = databaseUrl + "/databases/" + DefaultDatabase; } return(new ServerClient(databaseUrl, Conventions, ApiKey, Credentials, GetReplicationInformerForDatabase, null, jsonRequestFactory, currentSessionId, listeners.ConflictListeners)); }; #endif #if SILVERLIGHT // required to ensure just a single auth dialog var task = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(null, (Url + "/docs?pageSize=0").NoCache(), "GET", new OperationCredentials(ApiKey, Credentials), Conventions)) .ExecuteRequestAsync(); jsonRequestFactory.ConfigureRequest += (sender, args) => { args.JsonRequest.WaitForTask = task; }; #endif asyncDatabaseCommandsGenerator = () => { var asyncServerClient = new AsyncServerClient(Url, Conventions, ApiKey, Credentials, jsonRequestFactory, currentSessionId, GetReplicationInformerForDatabase, null, listeners.ConflictListeners); if (string.IsNullOrEmpty(DefaultDatabase)) { return(asyncServerClient); } return(asyncServerClient.ForDatabase(DefaultDatabase)); }; }
/// <summary> /// Initialize the document store access method to RavenDB /// </summary> protected virtual void InitializeInternal() { #if !SILVERLIGHT var rootDatabaseUrl = MultiDatabase.GetRootDatabaseUrl(Url); var rootServicePoint = ServicePointManager.FindServicePoint(new Uri(rootDatabaseUrl)); rootServicePoint.UseNagleAlgorithm = false; rootServicePoint.Expect100Continue = false; rootServicePoint.ConnectionLimit = 256; databaseCommandsGenerator = () => { string databaseUrl = Url; if (string.IsNullOrEmpty(DefaultDatabase) == false) { databaseUrl = rootDatabaseUrl; databaseUrl = databaseUrl + "/databases/" + DefaultDatabase; } return(new ServerClient(databaseUrl, Conventions, credentials, GetReplicationInformerForDatabase, null, jsonRequestFactory, currentSessionId)); }; #endif #if !NET35 #if SILVERLIGHT // required to ensure just a single auth dialog var task = jsonRequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, (Url + "/docs?pageSize=0").NoCache(), "GET", credentials, Conventions)) .ExecuteRequestAsync(); #endif asyncDatabaseCommandsGenerator = () => { #if SILVERLIGHT var asyncServerClient = new AsyncServerClient(Url, Conventions, credentials, jsonRequestFactory, currentSessionId, task, GetReplicationInformerForDatabase, null); #else var asyncServerClient = new AsyncServerClient(Url, Conventions, credentials, jsonRequestFactory, currentSessionId, GetReplicationInformerForDatabase, null); #endif if (string.IsNullOrEmpty(DefaultDatabase)) { return(asyncServerClient); } return(asyncServerClient.ForDatabase(DefaultDatabase)); }; #endif }
/// <summary> /// Puts the index definition for the specified name asyncronously /// </summary> /// <param name="name">The name.</param> /// <param name="indexDef">The index def.</param> /// <param name="overwrite">Should overwrite index</param> public Task <string> PutIndexAsync(string name, IndexDefinition indexDef, bool overwrite) { string requestUri = url + "/indexes/" + name; var webRequest = (HttpWebRequest)WebRequest.Create(requestUri); AddOperationHeaders(webRequest); webRequest.Method = "HEAD"; webRequest.Credentials = credentials; return(webRequest.GetResponseAsync() .ContinueWith(task => { try { task.Result.Close(); if (overwrite == false) { throw new InvalidOperationException("Cannot put index: " + name + ", index already exists"); } } catch (WebException e) { var response = e.Response as HttpWebResponse; if (response == null || response.StatusCode != HttpStatusCode.NotFound) { throw; } } var request = jsonRequestFactory.CreateHttpJsonRequest(this, requestUri, "PUT", credentials, convention); request.AddOperationHeaders(OperationsHeaders); var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters); byte[] bytes = Encoding.UTF8.GetBytes(serializeObject); return Task.Factory.FromAsync(request.BeginWrite, request.EndWrite, bytes, null) .ContinueWith(writeTask => Task.Factory.FromAsync <string>(request.BeginReadResponseString, request.EndReadResponseString, null) .ContinueWith(readStrTask => { var obj = new { index = "" }; obj = JsonConvert.DeserializeAnonymousType(readStrTask.Result, obj); return obj.index; })).Unwrap(); }).Unwrap()); }
internal static async Task <Stream> DownloadAsyncImpl(IHoldProfilingInformation self, HttpJsonRequestFactory requestFactory, FilesConvention conventions, NameValueCollection operationsHeaders, string path, string filename, Reference <RavenJObject> metadataRef, long? @from, long?to, string baseUrl, OperationCredentials credentials) { var request = requestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(self, baseUrl + path + Uri.EscapeDataString(filename), "GET", credentials, conventions)).AddOperationHeaders(operationsHeaders); if (@from != null) { if (to != null) { request.AddRange(@from.Value, to.Value); } else { request.AddRange(@from.Value); } } try { var response = await request.ExecuteRawResponseAsync().ConfigureAwait(false); if (response.StatusCode == HttpStatusCode.NotFound) { throw new FileNotFoundException("The file requested does not exists on the file system.", baseUrl + path + filename); } await response.AssertNotFailingResponse().ConfigureAwait(false); if (metadataRef != null) { metadataRef.Value = response.HeadersToObject(); } return(new DisposableStream(await response.GetResponseStreamWithHttpDecompression().ConfigureAwait(false), request.Dispose)); } catch (Exception e) { throw e.SimplifyException(); } }
private TcpConnectionInfo GetTcpInfo() { var convention = new DocumentConvention(); //since we use it only once when the connection is initialized, no reason to keep requestFactory around for long using (var requestFactory = new HttpJsonRequestFactory(1)) using (var request = requestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(null, string.Format("{0}/info/tcp", MultiDatabase.GetRootDatabaseUrl(_destination.Url)), HttpMethod.Get, new OperationCredentials(_destination.ApiKey, CredentialCache.DefaultCredentials), convention) { Timeout = TimeSpan.FromSeconds(15) })) { var result = request.ReadResponseJson(); var tcpConnectionInfo = convention.CreateSerializer().Deserialize <TcpConnectionInfo>(new RavenJTokenReader(result)); if (_log.IsInfoEnabled) { _log.Info($"Will replicate to {_destination.Database} @ {_destination.Url} via {tcpConnectionInfo.Url}"); } return(tcpConnectionInfo); } }
void LoadPlugins() { var jsonRequestFactory = new HttpJsonRequestFactory(); var baseUrl = (Address + "/silverlight/plugins").NoCache(); var credentials = new NetworkCredential(); var convention = new DocumentConvention(); var request = jsonRequestFactory.CreateHttpJsonRequest(this, baseUrl, "GET", credentials, convention); var response = request.ReadResponseStringAsync(); response.ContinueWith(_ => Execute.OnUIThread(() => { { var urls = from item in JArray.Parse(_.Result) let url = item.Value <string>() select url; var catalogs = from url in urls let fullUrl = Address + "/silverlight/plugin" + url.Replace('\\', '/') let uri = new Uri(fullUrl, UriKind.Absolute) select new DeploymentCatalog(uri); foreach (var deployment in catalogs) { deployment.DownloadCompleted += (s, e) => { if (e.Error != null) { throw e.Error; } }; deployment.DownloadAsync(); catalog.Catalogs.Add(deployment); } } })); }
private async Task EstablishConnection() { if (disposed) { return; } #if !NETFX_CORE if (clientSideHeartbeatTimer != null) { clientSideHeartbeatTimer.Dispose(); clientSideHeartbeatTimer = null; } #endif var requestParams = new CreateHttpJsonRequestParams(null, url + "/changes/events?id=" + id, "GET", credentials, conventions) { AvoidCachingRequest = true, DisableRequestCompression = true }; logger.Info("Trying to connect to {0} with id {1}", requestParams.Url, id); bool retry = false; IObservable <string> serverEvents = null; try { serverEvents = await jsonRequestFactory.CreateHttpJsonRequest(requestParams).ServerPullAsync().ConfigureAwait(false); } catch (Exception e) { logger.WarnException("Could not connect to server: " + url + " and id " + id, e); Connected = false; ConnectionStatusChanged(this, EventArgs.Empty); if (disposed) { throw; } bool timeout; if (replicationInformer.IsServerDown(e, out timeout) == false) { throw; } if (replicationInformer.IsHttpStatus(e, HttpStatusCode.NotFound, HttpStatusCode.Forbidden, HttpStatusCode.ServiceUnavailable)) { throw; } logger.Warn("Failed to connect to {0} with id {1}, will try again in 15 seconds", url, id); retry = true; } if (retry) { await Time.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); await EstablishConnection().ConfigureAwait(false); return; } if (disposed) { Connected = false; ConnectionStatusChanged(this, EventArgs.Empty); throw new ObjectDisposedException("RemoteDatabaseChanges"); } Connected = true; ConnectionStatusChanged(this, EventArgs.Empty); connection = (IDisposable)serverEvents; serverEvents.Subscribe(this); #if !NETFX_CORE clientSideHeartbeatTimer = new Timer(ClientSideHeartbeat, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); #endif if (watchAllDocs) { await Send("watch-docs", null).ConfigureAwait(false); } if (watchAllIndexes) { await Send("watch-indexes", null).ConfigureAwait(false); } foreach (var watchedDoc in watchedDocs) { await Send("watch-doc", watchedDoc).ConfigureAwait(false); } foreach (var watchedPrefix in watchedPrefixes) { await Send("watch-prefix", watchedPrefix).ConfigureAwait(false); } foreach (var watchedCollection in watchedCollections) { await Send("watch-collection", watchedCollection); } foreach (var watchedType in watchedTypes) { await Send("watch-type", watchedType); } foreach (var watchedIndex in watchedIndexes) { await Send("watch-indexes", watchedIndex).ConfigureAwait(false); } foreach (var watchedBulkInsert in watchedBulkInserts) { await Send("watch-bulk-operation", watchedBulkInsert).ConfigureAwait(false); } }
private bool ShouldExecuteUsing(string serverUrl, string counterStoreName, OperationCredentials credentials, CancellationToken token) { var failureCounter = failureCounters.GetHolder(serverUrl); if (failureCounter.Value == 0) { return(true); } if (failureCounter.ForceCheck) { return(true); } var currentTask = failureCounter.CheckDestination; if (currentTask.Status != TaskStatus.Running && delayTimeInMiliSec > 0) { var checkDestination = new Task(async delegate { for (int i = 0; i < 3; i++) { token.ThrowCancellationIfNotDefault(); try { var r = await TryExecuteOperationAsync <object>(serverUrl, counterStoreName, async(url, name) => { var serverCheckUrl = GetServerCheckUrl($"{url}/cs/{name}"); var requestParams = new CreateHttpJsonRequestParams(null, serverCheckUrl, HttpMethods.Post, credentials, CountersConventions); using (var request = requestFactory.CreateHttpJsonRequest(requestParams)) { await request.ReadResponseJsonAsync().WithCancellation(token).ConfigureAwait(false); } return(null); }, true, credentials, token).ConfigureAwait(false); if (r.Success) { failureCounters.ResetFailureCount(serverUrl); return; } } catch (ObjectDisposedException) { return; // disposed, nothing to do here } catch (Exception) { // safely ignore and let it try once again } await Task.Delay(delayTimeInMiliSec, token).ConfigureAwait(false); } }); var old = Interlocked.CompareExchange(ref failureCounter.CheckDestination, checkDestination, currentTask); if (old == currentTask) { checkDestination.Start(TaskScheduler.Default); } } return(false); }
private void ResolveConflictsCore(StraightforwardConflictResolution resolution, Func <IDocumentStore, string, bool> assertFunc, bool deleteLocal = false, bool deleteRemote = false) { using (var remote = CreateStore()) using (var local = CreateStore()) { TellFirstInstanceToReplicateToSecondInstance(); string id; using (var session = local.OpenSession()) { var company = new Company { Name = "Local" }; session.Store(company); session.SaveChanges(); id = session.Advanced.GetDocumentId(company); } if (deleteLocal) { using (var session = local.OpenSession()) { session.Delete(id); session.SaveChanges(); } } string remoteId; using (var session = remote.OpenSession()) { var company = new Company { Name = "Remote" }; session.Store(company); session.SaveChanges(); remoteId = session.Advanced.GetDocumentId(company); } if (deleteRemote) { using (var session = remote.OpenSession()) { session.Delete(remoteId); session.SaveChanges(); } } Assert.True(WaitForConflictDocumentsToAppear(local, id, local.DefaultDatabase), "Waited too long for conflict to be created, giving up."); using (var session = local.OpenSession()) { session.Store(new ReplicationConfig() { DocumentConflictResolution = resolution }, Constants.RavenReplicationConfig); session.SaveChanges(); } //Making sure the conflict index is up and running using (var session = local.OpenSession()) { var res = session.Query <dynamic>(Constants.ConflictDocumentsIndex).Count(); } var requestFactory = new HttpJsonRequestFactory(10); var request = requestFactory.CreateHttpJsonRequest( new CreateHttpJsonRequestParams(null, local.Url.ForDatabase(local.DefaultDatabase) + "/replication/forceConflictResolution" , HttpMethod.Get, local.DatabaseCommands.PrimaryCredentials, local.Conventions)); //Sometimes the conflict index doesn't index fast enough and we would fail if not waiting for indexes WaitForIndexing(local); request.ExecuteRequest(); var disapear = WaitForConflictDocumentsToDisappear(local, id, local.DefaultDatabase); Assert.True(disapear, $"Waited 15 seconds for conflict to be resolved but there is still a conflict for {id}"); Assert.True(assertFunc(local, id), "Conflict was resolved but the expected value is wrong"); } }
public async Task <RdcStats> GetRdcStatsAsync() { var requestUriString = baseUrl + "/rdc/stats"; using (var request = RequestFactory.CreateHttpJsonRequest(new CreateHttpJsonRequestParams(this, requestUriString, HttpMethod.Get, Credentials, Conventions)).AddOperationHeaders(OperationsHeaders)) { try { var response = (RavenJObject)await request.ReadResponseJsonAsync().ConfigureAwait(false); return(response.JsonDeserialization <RdcStats>()); } catch (Exception e) { throw e.SimplifyException(); } } }
/// <summary> /// Perform a single POST requst containing multiple nested GET requests /// </summary> public Task <GetResponse[]> MultiGetAsync(GetRequest[] requests) { var postedData = JsonConvert.SerializeObject(requests); var httpJsonRequest = jsonRequestFactory.CreateHttpJsonRequest(this, url + "/multi_get/", "POST", credentials, convention); return(httpJsonRequest.WriteAsync(postedData) .ContinueWith( task => { task.Wait(); // will throw if write errored return httpJsonRequest.ReadResponseStringAsync() .ContinueWith(replyTask => JsonConvert.DeserializeObject <GetResponse[]>(replyTask.Result)); }) .Unwrap()); }
private async Task EstablishConnection() { if (disposed) { return; } if (clientSideHeartbeatTimer != null) { clientSideHeartbeatTimer.Dispose(); clientSideHeartbeatTimer = null; } var requestParams = new CreateHttpJsonRequestParams(null, url + "/changes/events?id=" + id, "GET", credentials, conventions) { AvoidCachingRequest = true, DisableRequestCompression = true }; logger.Info("Trying to connect to {0} with id {1}", requestParams.Url, id); bool retry = false; IObservable <string> serverEvents = null; try { serverEvents = await jsonRequestFactory.CreateHttpJsonRequest(requestParams) .ServerPullAsync().ConfigureAwait(false); } catch (Exception e) { logger.WarnException("Could not connect to server: " + url + " and id " + id, e); Connected = false; ConnectionStatusChanged(this, EventArgs.Empty); if (disposed) { throw; } bool timeout; if (replicationInformer.IsServerDown(e, out timeout) == false) { throw; } if (replicationInformer.IsHttpStatus(e, HttpStatusCode.NotFound, HttpStatusCode.Forbidden, HttpStatusCode.ServiceUnavailable)) { throw; } logger.Warn("Failed to connect to {0} with id {1}, will try again in 15 seconds", url, id); retry = true; } if (retry) { await Time.Delay(TimeSpan.FromSeconds(15)).ConfigureAwait(false); await EstablishConnection().ConfigureAwait(false); return; } if (disposed) { Connected = false; ConnectionStatusChanged(this, EventArgs.Empty); throw new ObjectDisposedException(this.GetType().Name); } Connected = true; ConnectionStatusChanged(this, EventArgs.Empty); connection = (IDisposable)serverEvents; serverEvents.Subscribe(this); clientSideHeartbeatTimer = new Timer(ClientSideHeartbeat, null, TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(10)); await SubscribeOnServer(); }
private Task EstablishConnection() { if (disposed) { return(new CompletedTask()); } var requestParams = new CreateHttpJsonRequestParams(null, url + "/changes/events?id=" + id, "GET", credentials, conventions) { AvoidCachingRequest = true }; return(jsonRequestFactory.CreateHttpJsonRequest(requestParams) .ServerPullAsync() .ContinueWith(task => { if (disposed) { throw new ObjectDisposedException("RemoteDatabaseChanges"); } if (task.IsFaulted) { logger.WarnException("Could not connect to server, will retry", task.Exception); Connected = false; ConnectionStatusChanged(this, EventArgs.Empty); if (disposed) { return task; } if (replicationInformer.IsServerDown(task.Exception) == false) { return task; } if (replicationInformer.IsHttpStatus(task.Exception, HttpStatusCode.NotFound, HttpStatusCode.Forbidden)) { return task; } return Time.Delay(TimeSpan.FromSeconds(15)) .ContinueWith(_ => EstablishConnection()) .Unwrap(); } Connected = true; ConnectionStatusChanged(this, EventArgs.Empty); connection = (IDisposable)task.Result; task.Result.Subscribe(this); Task prev = watchAllDocs ? Send("watch-docs", null) : new CompletedTask(); if (watchAllIndexes) { prev = prev.ContinueWith(_ => Send("watch-indexes", null)); } prev = watchedDocs.Aggregate(prev, (cur, docId) => cur.ContinueWith(task1 => Send("watch-doc", docId))); prev = watchedPrefixes.Aggregate(prev, (cur, prefix) => cur.ContinueWith(task1 => Send("watch-prefix", prefix))); prev = watchedIndexes.Aggregate(prev, (cur, index) => cur.ContinueWith(task1 => Send("watch-indexes", index))); return prev; }) .Unwrap()); }
/// <summary> /// Puts the index definition for the specified name asynchronously /// </summary> /// <param name="name">The name.</param> /// <param name="indexDef">The index def.</param> /// <param name="overwrite">Should overwrite index</param> public Task <string> PutIndexAsync(string name, IndexDefinition indexDef, bool overwrite) { string requestUri = url + "/indexes/" + name; var webRequest = (HttpWebRequest)WebRequest.Create(requestUri); AddOperationHeaders(webRequest); webRequest.Method = "HEAD"; webRequest.Credentials = credentials; return(Task <WebResponse> .Factory.FromAsync(webRequest.BeginGetResponse, webRequest.EndGetResponse, null) .ContinueWith(task => { try { task.Result.Close(); if (overwrite == false) { throw new InvalidOperationException("Cannot put index: " + name + ", index already exists"); } } catch (AggregateException e) { var we = e.ExtractSingleInnerException() as WebException; if (we == null) { throw; } var response = we.Response as HttpWebResponse; if (response == null || response.StatusCode != HttpStatusCode.NotFound) { throw; } } var request = jsonRequestFactory.CreateHttpJsonRequest(this, requestUri, "PUT", credentials, convention); request.AddOperationHeaders(OperationsHeaders); var serializeObject = JsonConvert.SerializeObject(indexDef, Default.Converters); return Task.Factory.FromAsync(request.BeginWrite, request.EndWrite, serializeObject, null) .ContinueWith(writeTask => request.ReadResponseJsonAsync() .ContinueWith(readJsonTask => { return readJsonTask.Result.Value <string>("index"); })).Unwrap(); }).Unwrap()); }
public HttpJsonRequest CreateRequest(string relativeUrl, string method) { return(jsonRequestFactory.CreateHttpJsonRequest(this, url + relativeUrl, method, credentials, convention)); }
IEnumerable <Task> ExportData(SaveFileDialog saveFile, bool indexesOnly) { Console.Add("Exporting to {0}", saveFile.SafeFileName); var stream = saveFile.OpenFile(); var jsonRequestFactory = new HttpJsonRequestFactory(); var baseUrl = server.CurrentDatabaseAddress; var credentials = new NetworkCredential(); var convention = new DocumentConvention(); var streamWriter = new StreamWriter(new GZipStream(stream, CompressionMode.Compress)); var jsonWriter = new JsonTextWriter(streamWriter) { Formatting = Formatting.Indented }; Console.Add("Begin reading indexes"); jsonWriter.WriteStartObject(); jsonWriter.WritePropertyName("Indexes"); jsonWriter.WriteStartArray(); int totalCount = 0; const int batchSize = 128; var completed = false; while (!completed) { var url = (baseUrl + "/indexes/?start=" + totalCount + "&pageSize=" + batchSize).NoCache(); var request = jsonRequestFactory.CreateHttpJsonRequest(this, url, "GET", credentials, convention); var response = request.ReadResponseStringAsync(); yield return(response); var documents = response.Result; var array = JArray.Parse(documents); if (array.Count == 0) { Console.Add("Done with reading indexes, total: {0}", totalCount); completed = true; } else { totalCount += array.Count; Console.Add("Reading batch of {0,3} indexes, read so far: {1,10:#,#}", array.Count, totalCount); foreach (JToken item in array) { item.WriteTo(jsonWriter); } } } jsonWriter.WriteEndArray(); jsonWriter.WritePropertyName("Docs"); jsonWriter.WriteStartArray(); if (indexesOnly) { Console.Add("Documents will not be exported."); } else { Console.Add("Begin reading documents."); var lastEtag = Guid.Empty; totalCount = 0; completed = false; while (!completed) { var url = (baseUrl + "/docs/?pageSize=" + batchSize + "&etag=" + lastEtag).NoCache(); var request = jsonRequestFactory.CreateHttpJsonRequest(this, url, "GET", credentials, convention); var response = request.ReadResponseStringAsync(); yield return(response); var array = JArray.Parse(response.Result); if (array.Count == 0) { Console.Add("Done with reading documents, total: {0}", totalCount); completed = true; } else { totalCount += array.Count; Console.Add("Reading batch of {0,3} documents, read so far: {1,10:#,#}", array.Count, totalCount); foreach (JToken item in array) { item.WriteTo(jsonWriter); } lastEtag = new Guid(array.Last.Value <JObject>("@metadata").Value <string>("@etag")); } } } Execute.OnUIThread(() => { jsonWriter.WriteEndArray(); jsonWriter.WriteEndObject(); streamWriter.Flush(); streamWriter.Dispose(); stream.Dispose(); }); }