GeoObjectStore connection handling
Connecting to a GeoObjectStore service and selecting a store for a draw object layer are separate operations. A successful service connection does not automatically select or register the store to display.
Service, store and list
- Service URI identifies the GeoObjectStore gRPC endpoint.
- Store ID identifies a store within that service.
- List/container ID identifies a collection within a store.
The draw object layer's GeoStoreId selects its active store. Changing this property alone does not establish a service connection or create a store.
Use the gRPC endpoint, including any configured proxy prefix. For example, the default MariaServiceHoster gRPC front door uses http://server:5111/geostore/. Do not substitute the HTTP/Swagger listener; deployed addresses may differ.
Recommended sequence
- Finish or cancel active editing before switching services.
- Await
ConnectToServiceAsync(serviceUri)and check its result. - Discover available stores with
GetServerStoresAsync(), or use a known store ID. - Await
ConnectToServiceStoreAsync(storeId)to register the existing store locally. Check for a null result. - Set the draw object layer's
GeoStoreIdto that store ID. - Refresh application store/list selectors and check any active filters.
Use the GeoObjectClientStores instance belonging to the layer, not a separately created client-store manager.
Example: selecting an existing server store
The following belongs in a view model with an existing _drawObjectLayer. Call and await it from the UI thread. Disable overlapping connection changes, or serialize them as shown. All service-switch operations sharing this client-store manager must use the same coordination.
private readonly SemaphoreSlim _storeSwitchLock = new(1, 1);
public async Task SwitchGeoStoreAsync(Uri serviceUri, string storeId)
{
if (serviceUri == null || !serviceUri.IsAbsoluteUri ||
(serviceUri.Scheme != Uri.UriSchemeHttp &&
serviceUri.Scheme != Uri.UriSchemeHttps))
throw new ArgumentException("An HTTP/HTTPS service URI is required.",
nameof(serviceUri));
if (string.IsNullOrWhiteSpace(storeId))
throw new ArgumentException("A store ID is required.", nameof(storeId));
await _storeSwitchLock.WaitAsync();
try
{
var stores = _drawObjectLayer.GeoObjectClientStores;
if (!await stores.ConnectToServiceAsync(serviceUri))
throw new InvalidOperationException(
$"Could not connect to GeoObjectStore: {serviceUri}");
var store = await stores.ConnectToServiceStoreAsync(storeId);
if (store == null)
throw new InvalidOperationException(
$"Could not connect to store '{storeId}' at {serviceUri}.");
_drawObjectLayer.GeoStoreId = store.StoreId;
}
finally
{
_storeSwitchLock.Release();
}
}
The caller must await the task, report failures to the user and update connection/selection state. A failed switch is not a transaction that automatically restores the previous connection.
This example connects an existing server store. It deliberately does not create a missing store. Use the explicit store-creation API only when creation is intended and permitted.
Important switching concerns
- Do not select a store before connecting. Assigning a transient-store constant before an asynchronous connection completes can leave the layer targeting an unregistered or unintended store. Select an actual store on the destination service.
- Avoid fire-and-forget connection updates.
Task.Runis unnecessary for the asynchronous connection API. Unawaited updates can overlap, complete out of order and leave UI state inconsistent. - Switching replaces the backing service.
ConnectToServiceAsyncclears and disposes registered service-backed client stores. Do not retain old client-store references; reacquire them after switching. Local in-memory stores are retained. - The manager may be shared. Changing its service affects every layer using that manager. Coordinate those layers rather than treating the operation as private to one view model.
- Store IDs are not server identities. Two servers can contain the same store ID. Do not skip reconnecting merely because
GeoStoreIdalready has the desired value. - Avoid local/server ID collisions. A local in-memory store with the same ID can mask the intended server store.
- Registration is not selection.
GetServerStoresAsync()discovers server stores;GetClientStores()lists locally registered stores. Neither selects the layer's active store. - Setup may create a store.
SetupGeoObjectStorecan create a missing server store. PreferConnectToServiceStoreAsyncwhen connecting must not create data.
Connection lifecycle and troubleshooting
Observe Connected, Disconnected, ErrorOccurred, ServiceConnectionChanged and ServiceRestarted as appropriate. Marshal UI changes to the UI thread, refresh store/list choices after relevant changes, and define an explicit fallback policy when the active server store becomes unavailable.
If connected objects are not visible, check:
- The selected store exists and is registered on the current service.
- The layer's
GeoStoreIdmatches the intended store. - List/container selections, display filters, visibility, scale and viewport are appropriate.
- The endpoint supports gRPC and the expected route; HTTPS certificates and network access are valid.
Reference implementation
MariaApplication demonstrates service connection, store registration, active-store selection and lifecycle handling in GeoObjectClientStoresViewModel.cs (repository access required).
See also Draw object layer for the relationship between shared draw object resources and individual layers. API availability and lifecycle behavior should be checked against the deployed GDK version.