Overview
OData
OData integration in the framework for metadata-driven routes, with controlled fallback to alternative providers. Full read + CRUD + $expand + $count=true wrapper support.
Scope
- Using OData endpoints as a data source for
wuic-data-source. - Alignment with table/column metadata for UI rendering and runtime behavior.
- Relationship with the initial scaffolding flow and project bootstrap.
- Navigation properties auto-generated from
lookupByIDcolumns to enable native$expand.
Examples
- Pattern 3c — OData Cities grid (read + CRUD + `$expand` + pure-OData lookup) — full example with
$top/$skip/$filter/$orderby/$expand/$count=trueagainst/odata/Citiesand/odata/Stateprovinces.
Metadata References
- Routing and base configuration remain driven by metadata (
md_route_name, columns, filters, sort, paging). - In
md_props_bagyou can force the provider viaendpoint(example:{"endpoint":{"type":"odata","uri":"/odata/Orders"}}). - Lookup columns can also point to a custom OData endpoint via
mc_props_bag.lookup.endpoint(see Widget Lookup). - The datasource continues to use
filterInfo,sortInfo,pageInfo; on the OData provider side, translation into an OData query occurs. - For cross-cutting metadata details see Metadata.
Auto-generated Navigation Properties (lookupByID → $expand)
The dynamic EF generator (MetadataModelGenerator) automatically emits a navigation property on every column with mc_ui_column_type = 'lookupByID' and mc_ui_lookup_entity_name pointing to a route exposed in WebAPI. Naming convention: strip the Id / ID suffix from the FK name → nav property name (e.g., stateProvinceID → StateProvince). With EnableLowerCamelCase() active, the EDM exposes the nav in camelCase (stateProvince).
Practical effects:
- the
$metadatacontains<NavigationProperty Name="stateProvince" Type="...Stateprovinces" />on theCitiesentity; GET /odata/Cities?$expand=stateProvincereturns each row with the related object inline ({... "stateProvince": { "stateProvinceID": 1, "stateProvinceName": "Alabama" }});- the
EntitiesController.Getcontroller translates$expand=navPropinto.Include(navProp)EF Core (SQL LEFT JOIN).
Current limits: only top-level $expand (no nested $expand=a($expand=b)); OData sub-options after the nav name (e.g., $expand=stateProvince($select=...)) are ignored — .Include loads the entire related entity.
Response Shape and $count=true Wrapper
- Default (without
$count): the endpoint returns a plain JSON array ([{...}, {...}]) for compatibility with legacy framework consumers (DataProviderOdataService.select,DataProviderWebserviceService, etc.) that readresponse as any[]. - Opt-in OData v4 (
$count=truein the query): the endpoint returns the standard wrapper{ "value": [...], "@odata.count": N }whereNis the total computed after$filter/$orderbybut before$skip/$top. Use when you need the paginated total (e.g., UI pager).
Full CRUD via EntitiesController
The generic OData endpoint exposes the entire RESTful suite on /odata/{EntitySet}:
GET /odata/{EntitySet}— list with$top/$skip/$filter/$orderby/$select/$expand/$count=true.POST /odata/{EntitySet}— insert, JSON body with columns (nullcolumns in the payload are skipped so SQL applies theDEFAULTDB-side, automatically supporting IDENTITY/SEQUENCE/GUID PKs and columns with temporal/audit defaults).PATCH /odata/{EntitySet}({key})— partial update, JSON body with subset of columns; PK in path.DELETE /odata/{EntitySet}({key})— delete by single key.
Gating on the metadata side (_metadati__tabelle row):
mdexposeinwebapi = 1(required for any CUD, otherwise 403).mdserviceenableinsert,mdserviceenableedit,mdserviceenabledelete= 1 for the respective operations.- See also the "Web service" metadata tab in the metadata-editor.
"Web Service" Tab Metadata (OData/API)
In the table metadata editor, the Web Service tab governs API exposure and operational gates on the service side.
md_expose_in_webapi: exposes the route in Web API/OData services.md_service_apply_default_filter: also appliesmd_default_filteron the service side.md_service_enable_delete: enables delete from the service endpoint.md_service_enable_edit: enables update/edit from the service endpoint.md_service_enable_insert: enables insert from the service endpoint.md_service_enable_detail: enables record detail endpoint.md_service_enable_clone: enables record clone on the service side.md_service_enable_logging: enables operation logging on the service side.md_service_page_size: service-side page size (Pagesizein the UI tab).
Recommended inline snippet:
md_expose_in_webapi=true,md_service_enable_insert=true,md_service_enable_edit=true,md_service_enable_delete=true,md_service_page_size=100.
Practical note:
md_pagesizecontrols the standard UI paging;md_service_page_sizecontrols service endpoint paging.- For OData, it is advisable to keep service flags aligned with the backend's actual capabilities (read-only vs read/write).
Scaffolding References
- The bootstrap (
firstRun) prepares the application context and initial connections; after scaffolding, you can attach routes to OData endpoints. - During initial setup, it is advisable to immediately validate routes, table/column metadata, and minimum mappings to avoid OData/metadata mismatches.
- Operational reference: Initial Scaffolding.
Recommended Operational Flow
1. Define/validate route metadata and minimum columns.
2. Configure the OData endpoint provider in md_props_bag.endpoint.
3. Verify list reading with filter/sort/paging from the UI.
4. If using OData auth, align appsettings keys (enableODATAAuthentication and related policies).
Practical Notes
- OData and metadata are not alternatives: metadata remains the source of UI behavior, OData the data source.
- In case of schema differences, first update column metadata (
mc_*) and then refine the OData endpoint/query.