Skip to content

DHI.Services.MCLite for Places — Internal Developer Guide

This page documents the MCLite repository implementation for the Places domain. It explains how the provider maps Place and Indicator entities to the MCLite schema, how to wire it into a service or the Web API, and what database objects must exist up-front.

Related docs: MCLite Providers · MCLite for GIS


What this provider is

DHI.Services.Provider.MCLite.PlaceRepository is an implementation of IPlaceRepository<string> that persists Places and Indicators into an MCLite database (PostgreSQL, SQL Server or SQLite via the shared Db abstraction). Unlike our PostgreSQL provider, MCLite does not create tables for you—the schema must pre-exist.

Key capabilities:

  • Create/update/delete places and persist indicators (time series–based only; scalar indicators are skipped)
  • De-duplicate and normalize palette styles and time intervals at the place collection level
  • Query by group, including non-recursive lookups and group options
  • Read back a place and reconstruct its indicators (including expanding per-collection time intervals)

Warning

Because palettes and intervals are normalized, a single indicator definition may produce multiple materialized indicators at read time (one per interval). Names are automatically suffixed (-1, -2, …) to avoid collisions.


Connection string & workspace

Instantiate the provider with an MCLite connection string:

var repo = new DHI.Services.Provider.MCLite.PlaceRepository(
  "database=mc2014.2;workspace=workspace2"
);

Supported parameters (see MCLite Providers for full list):

  • database (required) — DB name (or SQLite file path)
  • workspace (required) — logical workspace; resolved to a schema name via master.workspace
  • Optional: host (default localhost), port (Pg default 5432), username (default dss_admin), password (default secretdss_admin), dbflavour=PostgreSQL|SqlServer|SQLite, cachemode=All|...

Workspace resolution requirement The provider resolves your effective schema with:

SELECT schema_name FROM master.workspace WHERE name = @Name

Make sure the master catalog exists and contains an entry mapping workspaceschema_name.


Required database schema

These table names/fields are referenced by the provider through Names.* constants. If you are using a standard MCLite database, you already have these. Otherwise, create them accordingly. Column types below are provided for PostgreSQL; adapt to SQL Server/SQLite as needed.

1) Place collections & places

{schema}.place_collection

Column Type Notes
id uuid PK
group_id uuid NULL Parent collection group (nullable root)
name text Collection name
place_collection_attribute_key text Feature attribute key used by places
feature_class_id uuid FK {schema}.feature_class(id) (GIS)
version uuid Row version (GUID)

{schema}.place

Column Type Notes
id uuid PK
name text Friendly name (defaults to feature attribute)
attribute_value text Feature attribute value
feature_id uuid FK → GIS feature row
place_collection_id uuid FK {schema}.place_collection(id)
category text Optional (taken from metadata key containing “category”)
version uuid Row version

GIS dependency: the provider uses FeatureRepository to validate features. See MCLite for GIS for the GIS schema and configuration.

2) Indicators, styles, thresholds, time intervals

{schema}.place_indicator

Column Type Notes
id uuid PK
name text Indicator type (e.g., Rainfall)
aggregation_type int AggregationType.Value
time_series_type int Reserved (set 0)
time_series_id_type int Reserved (set 0)
data_type int DataSourceType enum value (TimeSeries only; scalars are skipped)
quantile double Optional
time_series_id text DataSource.EntityId
place_id uuid FK {schema}.place(id)
connection_info text DataSource.ConnectionId (e.g., csv)
version uuid Row version

{schema}.place_indicator_style (palette colors, de-duplicated per place collection)

Column Type Notes
id uuid PK
name text Palette band label (e.g., 0, 10, Low)
color text HTML color (e.g., #FF0000)
place_collection_id uuid FK {schema}.place_collection(id)
version uuid

{schema}.place_indicator_threshold (palette thresholds per indicator)

Column Type Notes
id uuid PK
threshold_index int 1-based order of thresholds
threshold_value double Palette threshold value
indicator_style_id uuid FK {schema}.place_indicator_style(id)
place_indicator_id uuid FK {schema}.place_indicator(id)
version uuid

{schema}.place_time_interval (distinct intervals per place collection)

Column Type Notes
id uuid PK
name text e.g., Interval(1)
interval_type int TimeIntervalType enum
start_time double? Required for non-All
end_time double? Required for non-All
place_collection_id uuid FK {schema}.place_collection(id)
version uuid

3) Shared metadata tables used by the provider

{schema}.entity_type → looked up by name (e.g., Place) {schema}.entity_descriptionid (entity id), entity_type_id {schema}.metadataid, entity_description_id, metadata_values (stored as key:value;key:value;…)

The provider automatically inserts a row into entity_description and metadata after a successful place insert.


Data mapping: domain → MCLite

Domain MCLite column(s) Notes
Place.Id (FullName) place.id Guid generated by repo; returned in reads
Place.Name place.name Defaults to FeatureId.AttributeValue if empty
Place.Group derived from collection place_collection row + group hierarchy
Place.FeatureId place.feature_id, place_collection.feature_class_id, place_collection.place_collection_attribute_key, place.attribute_value Validated via GIS FeatureRepository
Place.Metadata metadata.metadata_values Stored flattened as key:value;
Indicator.Key (type) place_indicator.name We treat “type” as “name”
Indicator.DataSource place_indicator.connection_info, place_indicator.time_series_id, place_indicator.data_type Scalars are skipped on write
Indicator.AggregationType.Value place_indicator.aggregation_type
Indicator.Quantile place_indicator.quantile nullable
Indicator.StyleCode place_indicator_style + place_indicator_threshold Styles deduped per collection
Indicator.TimeInterval place_time_interval Intervals deduped per collection; indicators are expanded at read time per interval

Behavior & query semantics

  • Add(place): inside a transaction, the repo:
    1. Ensures place_collection exists (creates the group path if needed),
    2. Inserts the place,
    3. Inserts non-scalar indicators,
    4. Upserts place-level time intervals and styles,
    5. Links thresholds,
    6. Commits, then writes entity description + metadata.
  • Update(place): remove then re-add (per existing group & name).
  • Get(id or "Group/Name"): materializes the place; reconstructs indicators by joining:
    • place_indicator × place_time_interval × thresholds/styles (expands one per interval).
  • GetByGroup(group):
    • group can be a place collection group path (recursive)
    • or a collection path when suffixed with ;nonrecursive.
  • ContainsGroup(group): accepts tree options embedded in group (e.g. ;nonrecursive, ;groupsonly).
  • Scalars: indicators with DataSourceType.Scalar are ignored on write.

Wiring it up

You can wire the MCLite provider either manually (DI/ServiceLocator) or through the Connections module (config-driven).

A) Manual registration (ServiceLocator.Register)

// GIS (MCLite) — required by Places
ServiceLocator.Register(
  new DHI.Services.GIS.GisService<string>(
    new DHI.Services.Provider.MCLite.FeatureRepository("database=mc2014.2;workspace=workspace2")
  ),
  "feature-mclite");

// Time series — pick your implementation; CSV shown for simplicity
ServiceLocator.Register(
  new DHI.Services.TimeSeries.DiscreteTimeSeriesService<string,double>(
    new DHI.Services.TimeSeries.CSV.TimeSeriesRepository("[AppData]".Resolve())
  ),
  "csv");

// Compose Places with MCLite repo
var ts = new Dictionary<string, IDiscreteTimeSeriesService<string,double>> {
  ["csv"] = Services.Get<IDiscreteTimeSeriesService<string,double>>("csv")
};
var scalars = new Dictionary<string, IScalarService<string,int>>(); // optional
var gis = Services.Get<IGisService<string>>("feature-mclite");

ServiceLocator.Register(
  new DHI.Services.Places.PlaceService(
    new DHI.Services.Provider.MCLite.PlaceRepository("database=mc2014.2;workspace=workspace2"),
    ts, scalars, gis),
  "places-mclite" // ← your connectionId
);

// Now available via Web API as /api/places/places-mclite/...

You must have the GIS FeatureRepository configured against the same MCLite workspace. See MCLite for GIS.

B) Config-driven (Connections module)

{
  "$type": "System.Collections.Generic.Dictionary`2[[System.String, mscorlib],[DHI.Services.IConnection, DHI.Services]], mscorlib",

  "csv": {
    "$type": "DHI.Services.TimeSeries.WebApi.DiscreteTimeSeriesServiceConnection, DHI.Services.TimeSeries.WebApi",
    "ConnectionString": "[AppData]",
    "RepositoryType": "DHI.Services.TimeSeries.CSV.TimeSeriesRepository, DHI.Services.TimeSeries",
    "Name": "CSV time series service connection",
    "Id": "csv"
  },

  "feature-mclite": {
    "$type": "DHI.Services.GIS.WebApi.GroupedUpdatableGisServiceConnection, DHI.Services.GIS.WebApi",
    "ConnectionString": "database=mc2014.2;workspace=workspace2",
    "RepositoryType": "DHI.Services.Provider.MCLite.FeatureRepository, DHI.Services.Provider.MCLite",
    "Name": "MCLite GIS service connection",
    "Id": "feature-mclite"
  },

  "places-mclite": {
    "$type": "DHI.Services.Places.WebApi.PlaceServiceConnection, DHI.Services.Places.WebApi",
    "ConnectionString": "database=mc2014.2;workspace=workspace2",
    "RepositoryType": "DHI.Services.Provider.MCLite.PlaceRepository, DHI.Services.Provider.MCLite",
    "GisServiceConnectionId": "feature-mclite",
    "TimeSeriesServiceConnectionIds": [ "csv" ],
    "Name": "Place service connection",
    "Id": "places-mclite"
  }
}

Once loaded, your Web API exposes /api/places/places-mclite/....


Quickstart: seed data (from JSON or code)

You can migrate from a JSON definition like the sample places.json by reading it into Place/Indicator objects and calling placeService.Add(place). The provider will:

  • Create the place collection based on FullName (/Group/.../Collection/Name)
  • Validate feature existence via FeatureRepository (throws if not found)
  • Insert place and indicators (non-scalar)
  • Persist palette thresholds and time intervals at the collection level
  • Save metadata (flattened)

Tip: if the same indicator should exist across multiple intervals, specify a single indicator with a TimeInterval. Reads will expand it for each interval registered at the collection.


Operational notes

  • Transactions: Add and bulk Add/Update run within DB transactions per call.
  • Deletes: Update removes the old place then adds the new one.
  • Performance: GetAll() issues a handful of queries; prefer GetByGroup() for targeted reads.
  • Groups: pass "...;nonrecursive" to GetByGroup to restrict to the exact collection path.
  • Case & lengths: indicator names/types and time series IDs are stored as text; ensure any custom constraints in your DB match expected lengths.
  • Scalars: skipped (by design) in _InsertPlaceIndicatorInfo. If you require scalars, extend the schema and mapper.

Troubleshooting

  • “No feature is found.” — The (FeatureCollectionId, AttributeKey, AttributeValue) triple didn’t match any GIS feature. Verify your GIS connection (MCLite for GIS) and attribute names/values.
  • Intervals duplicated — The repo dedupes by (Type, Start, End) per collection. Confirm your input values (double equality) are identical.
  • Colors missing — Styles are keyed by "{BandText};{HtmlColor}". Ensure your StyleCode yields palettes with both parts.

Minimal DDL (PostgreSQL example)

Use if you’re standing up a dedicated workspace schema from scratch. This is intentionally minimal—most projects rely on the standard MCLite DB which already ships all tables.

-- master catalog
CREATE SCHEMA IF NOT EXISTS master;
CREATE TABLE IF NOT EXISTS master.workspace (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  name text UNIQUE NOT NULL,
  schema_name text NOT NULL
);

-- your workspace schema, e.g. "workspace2"
CREATE SCHEMA IF NOT EXISTS workspace2;

-- place collections
CREATE TABLE workspace2.place_collection (
  id uuid PRIMARY KEY,
  group_id uuid NULL,
  name text NOT NULL,
  place_collection_attribute_key text,
  feature_class_id uuid NOT NULL,
  version uuid NOT NULL
);

-- places
CREATE TABLE workspace2.place (
  id uuid PRIMARY KEY,
  name text NOT NULL,
  attribute_value text,
  feature_id uuid NOT NULL,
  place_collection_id uuid NOT NULL REFERENCES workspace2.place_collection(id),
  category text,
  version uuid NOT NULL
);

-- indicators
CREATE TABLE workspace2.place_indicator (
  id uuid PRIMARY KEY,
  name text NOT NULL,
  aggregation_type int NOT NULL,
  time_series_type int NOT NULL,
  time_series_id_type int NOT NULL,
  data_type int NOT NULL,
  quantile double precision,
  time_series_id text,
  place_id uuid NOT NULL REFERENCES workspace2.place(id),
  connection_info text,
  version uuid NOT NULL
);

-- time intervals
CREATE TABLE workspace2.place_time_interval (
  id uuid PRIMARY KEY,
  name text NOT NULL,
  interval_type int NOT NULL,
  start_time double precision,
  end_time double precision,
  place_collection_id uuid NOT NULL REFERENCES workspace2.place_collection(id),
  version uuid NOT NULL
);

-- styles
CREATE TABLE workspace2.place_indicator_style (
  id uuid PRIMARY KEY,
  name text NOT NULL,
  color text NOT NULL,
  place_collection_id uuid NOT NULL REFERENCES workspace2.place_collection(id),
  version uuid NOT NULL
);

-- thresholds
CREATE TABLE workspace2.place_indicator_threshold (
  id uuid PRIMARY KEY,
  threshold_index int NOT NULL,
  threshold_value double precision NOT NULL,
  place_indicator_style_id uuid NOT NULL REFERENCES workspace2.place_indicator_style(id),
  place_indicator_id uuid NOT NULL REFERENCES workspace2.place_indicator(id),
  version uuid NOT NULL
);

-- entity type/description + metadata (minimal)
CREATE TABLE workspace2.entity_type (
  id uuid PRIMARY KEY,
  name text UNIQUE NOT NULL
);
CREATE TABLE workspace2.entity_description (
  id uuid PRIMARY KEY,           -- equals the entity's id
  entity_type_id uuid NOT NULL REFERENCES workspace2.entity_type(id)
);
CREATE TABLE workspace2.metadata (
  id uuid PRIMARY KEY,
  entity_description_id uuid NOT NULL REFERENCES workspace2.entity_description(id),
  metadata_values text
);

Don’t forget to insert a master.workspace row mapping your logical workspace (e.g. workspace2) to the correct schema_name (e.g. workspace2).


Web API pairing

Once registered (manually or via connections), the Places Web API exposes the MCLite-backed service at:

/api/places/{connectionId}/...

All endpoints (CRUD on places/indicators, features with status colors, thresholds, palette PNG, etc.) are the same as for other providers. See the Web API guide for details.