Where to get a delta geodatabase for ImportGeodatabaseDeltaAsync?

1416
3
07-27-2017 08:54 AM
MaximilianGlas
Esri Contributor

In the Runtime .NET API, I found a method named ImportGeodatabaseDeltaAsync.

Unfortunately, there is no documentation how to use this method in a full workflow. So I have to try many things.

The first parameter is

geodatabaseFileName

Type: System.String
The path and filename of geodatabase where delta is applied to.

It seems that it has to be a Runtime geodatabase. I could use my Runtime geodatabase anyway, which I have created with my Runtime client against an sync enabled ArcGIS Server (FeatureServer) service. So, this is no problem, as I could create it with the Runtime SDK method GenerateGeodatabase.

The second parameter is

deltaGeodatabaseFileName

Type: System.String
The path and filename of geodatabase where to import the delta from

This one could not be a normal Runtime geodatabase file, because you will get an exception in that case:

Esri.ArcGISRuntime.ArcGISRuntimeException: 'SQL error or missing database: no such table: GDB_DataChangesDatasets'

Obiviously the geodatase has a different structure.

I could not find any SDK functionality for creating a delta package, so I tried to create one directly from the REST API:

We created a replica first, added a new feature in the service and then called the synchronizeReplica operation. The result was a geodatabase file, which now has different schema as there exists some additional tables in it.

When using this geodatabase for the deltaGeodatabaseFileName Parameter, the ImportGeodatabaseDeltaAsync works.

My question now is:

  • Is there a way to create the delta geodatabase file with Runtime SDK?
  • As the synchronizeReplica REST operation is also used SyncGeodatabase SDK method, do I have to use some special parameters to generate it? But how?
  • Could I also generate the delta geodatabase with a desktop/python tool?
  • As first parameter (geodatabaseFileName), could I also use a desktop generated geodatabase (ArcGIS runtime content)?

And maybe adding a documentation for this workflow would be helpful, as I could not find anything about it.

3 Replies
ChiragGoswami2
New Contributor

Hi Maximilian Glas‌,

Probably too late for this, but check this out:

http://desktop.arcgis.com/en/arcmap/10.3/tools/data-management-toolbox/export-to-delta.htm

It creates a delta using a child replica geodatabase.

0 Kudos
JoeHershman
MVP Regular Contributor

Also not sure if this is still a concern.

The way you generate the delta is with the rest API https://developers.arcgis.com/rest/services-reference/synchronize-replica.htm.  You could do through python or in C# with HttpClient.

This is some code that does in C#, most of everything is here, excluding some custom objects that define some of the basics which I think can be figured out.

//returns the Url of the delta file which is used by the calling method to download 
public async Task<string> SyncronizeReplicaAsync(ReplicaDefinition replicaDefinition)
{
	try
	{
		ArcGISHttpClientHandler handler = new ArcGISHttpClientHandler { ArcGISCredential = await _portalManager.Credential(true) };
		
		using ( var client = new HttpClient(handler) )
		{
			try
			{
				var featureServiceUrl = new Uri($"{FeatureServiceUrl(replicaDefinition)}?f=json");

				var get = await client.GetStringAsync(featureServiceUrl);
			}
			catch (Exception)
			{
				_log.Warn($"Could not connect to Url: {FeatureServiceUrl(replicaDefinition)}");
				return null;
			}
			
			//This gets the replica url wich is featureserviceUrl/replicaId
			var requestUri = new Uri($"{FeatureServiceUrl(replicaDefinition)}/synchronizeReplica");
                        
			var parameters = new Dictionary<string, string>
			{
				{"f", "json"},
				{"async", "true"},
				{"dataFormat", "sqlite"},
				{"replicaID", replicaDefinition.ReplicaId.ToLower()},
				{"rollbackOnFailure", "false"},
				{"syncLayers", await GetSyncLayersAsync(replicaDefinition)},
				{"transportType", "esriTransportTypeUrl"}
			};

			var content = new FormUrlEncodedContent(parameters);
			
			//This sends off the sync request to the server
			var response = await client.PostAsync(requestUri, content);

			if ( !(response.Content is ByteArrayContent byteArrayContent) ) return null;

			var json = await byteArrayContent.ReadAsStringAsync();

			var status = JsonConvert.DeserializeObject<StatusResponse>(json);
			var resultUrl = await CheckJobAsync(replicaDefinition, status);

			return resultUrl;
		}
	}
	catch (Exception e)
	{
		_log.Error(e.Message, e);
		return null;
	}
}



// I have some custom objects that mimic responses and use json deserializer to convert to .net objects
private async Task<string> CheckJobAsync(ReplicaDefinition replicaDefinition, StatusResponse status)
{
	JobResponse job = null;
	ArcGISHttpClientHandler handler = null;

	try
	{
		while ( true )
		{
			handler = new ArcGISHttpClientHandler {ArcGISCredential = await _portalManager.Credential(false)};
			using (var client = new HttpClient(handler))
			{
				//This is a periodic check of the Job status
				var requestUri = new Uri($"{status.StatusUrl}?f=json");
				var json = await client.GetStringAsync(requestUri);

				job = JsonConvert.DeserializeObject<JobResponse>(json);

				// When job is complete the respence incluses the Url of the delta database
				if ( job.JobStatus == JobStatus.Completed ) break;

				if ( job.JobStatus == JobStatus.Failed )
				{
					_log.Warn($"Job failed: {replicaDefinition.Name}");
					return null;
				}

				if ( job.JobStatus == JobStatus.Other )
				{
					_log.Warn($"Job unknown: {replicaDefinition.Name}");
					return null;
				}

				await Task.Delay(TimeSpan.FromSeconds(5));
			}
		}
	}
	catch (ArcGISWebException e)
	{
		if ( handler?.ArcGISCredential is TokenCredential tokenCredential )
		{
			_log.Error($"{e.Message}: {tokenCredential.ExpirationDate?.LocalDateTime:hh:mm:ss}");
		}
		else
		{
			_log.Error(e.Message, e);
		}

	}
	catch (Exception e)
	{
		_log.Error(e.Message, e);
	}

	return job?.ResultUrl;
}


// Gets the layer array required by the syncronizeReplica request
private async Task<string> GetSyncLayersAsync(ReplicaDefinition replicaDefinition)
{
	try
	{
		var handler = new ArcGISHttpClientHandler { ArcGISCredential = await _portalManager.Credential(false) };
		using ( var client = new HttpClient(handler){Timeout = TimeSpan.FromMinutes(5)} )
		{
			var requestUri = new Uri($"{FeatureServiceUrl(replicaDefinition)}/replicas/{replicaDefinition.ReplicaId}?f=json");
			var response = await client.GetStringAsync(requestUri);

			var jObject = JObject.Parse(response);
			var syncLayers = JsonConvert.DeserializeObject<IEnumerable<SyncLayer>>(jObject["layerServerGens"].ToString()).ToArray();

			foreach (var syncLayer in syncLayers)
			{
				syncLayer.SyncDirection = "download";
			}

			//Setting to allow to sync back beyond last sync time
			// The serverGen parameter is actually the time stamp that the syncronizeReplica looks back into the past
			// by default this is retrieved from the replica definition.
			//This is logic to allow us to push that time back in case we need to go further to the past
			if (_appSettings.DownloadSyncBack)
			{
				long time = (DateTimeOffset.UtcNow - TimeSpan.FromDays(_appSettings.DownloadDaysBack)).ToUnixTimeMilliseconds();

				foreach (var syncLayer in syncLayers)
				{
					syncLayer.ServerGen = time;
				}
			}

			var serializerSettings = new JsonSerializerSettings
			{
				ContractResolver = new CamelCasePropertyNamesContractResolver()
			};

			string json = JsonConvert.SerializeObject(syncLayers, serializerSettings);

			return json;
		}
	}
	catch (Exception e)
	{
		_log.Error(e.Message, e);
		return null;
	}
}
‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍‍
0 Kudos
Riyasdsrc
New Contributor II

Hi @MaximilianGlas , 

Esri.ArcGISRuntime.ArcGISRuntimeException: 'SQL error or missing database: no such table: GDB_DataChangesDatasets'

This is the error I am getting from the xamarin forms application, when I get to use ApplyDeltaAsync(geodatabase, geodbFilename) method available in runtime. 

 

As you mentioned, is there any way to generate the delta geodatabase using runtime sdk from the app itself or how do we solve this issue, it cloudy to understand and lack of samples also. Could you please help me move forward?

0 Kudos