GeoObjectStore connection handling

From Maria GDK Wiki
Jump to navigation Jump to search

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

  1. Finish or cancel active editing before switching services.
  2. Await ConnectToServiceAsync(serviceUri) and check its result.
  3. Discover available stores with GetServerStoresAsync(), or use a known store ID.
  4. Await ConnectToServiceStoreAsync(storeId) to register the existing store locally. Check for a null result.
  5. Set the draw object layer's GeoStoreId to that store ID.
  6. 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.Run is unnecessary for the asynchronous connection API. Unawaited updates can overlap, complete out of order and leave UI state inconsistent.
  • Switching replaces the backing service. ConnectToServiceAsync clears 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 GeoStoreId already 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. SetupGeoObjectStore can create a missing server store. Prefer ConnectToServiceStoreAsync when 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 GeoStoreId matches 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.