Zum Inhalt

OData und Facetten

Jede Entity wird mit einer Zeile zu einem OData-Set. Tabellen, Exporte und externe Werkzeuge lesen darüber; Listenabfragen schreiben Sie nicht.

[DependsOn(typeof(CoworkeeODataModule))]
public sealed class MyAppCatalogModule : CoworkeeModule
{
    public override void ConfigureServices(ModuleServiceContext context)
    {
        context.Services.AddODataEntity<Brand>("Brands", CatalogPermissions.Brands.View);
        context.Services.AddODataEntity<Product>("Products", CatalogPermissions.Products.View);
    }
}
Anfrage Ergebnis
GET /odata/Products?$filter=Rate gt 20&$orderby=Name&$top=25&$skip=50&$count=true eine Seite, Gesamtzahl
GET /odata/Products?$expand=Brand Produkte mit ihrer Marke
GET /odata/Products({id}) ein Produkt
GET /odata/Products?$select=Name,Rate nur diese Spalten

Die Berechtigung des Sets wird bei jedem Aufruf geprüft, Mandanten- und Soft-Delete-Filter gelten wie überall. $top ist auf 1000 begrenzt (Coworkee:OData:MaxTop); ungültige Abfragen beantworten 400 mit Problem Details.

Facetten

Markieren Sie die Eigenschaften, nach denen Benutzer filtern sollen:

using Nextended.Core.Facets;

public sealed class Product : AuditedEntity, IMultiTenant
{
    [ProvideFacet(Label = "Brand", ValuePath = "BrandId", LabelPath = "Brand.Name", ValueType = typeof(Guid))]
    public Guid BrandId { get; set; }

    public Brand? Brand { get; set; }
}

Fragt der Request mit Prefer: odata.include-annotations="cn.facets" danach, enthält die Seite die Facettengruppen unter @cn.facets: jede Option mit Beschriftung, Anzahl und dem OData-Filterfragment, das sie setzt. Die Zahlen folgen dem aktuellen Filter, wie man es von Shop-Filtern kennt. Enum-Optionen tragen den Membernamen als Wert und Filter (Status eq 'Shipped') und ihren [Display]-Namen als Beschriftung; [Flags]-Enums listen jedes Flag einzeln (Options has 'Express'). Zahlenbereiche und Datumswerte kommen mit kleinstem und größtem Wert und einem Histogramm. [FacetBehavior] neben [ProvideFacet] oder services.ConfigureFacet<T>(...) legt die Ansicht fest und wie Werte verknüpft werden (eines von, alle von, einzeln, mit Ausschluss). Mit dem Header Coworkee-Facets zählt eine Gruppe ohne ihre eigene Auswahl. CoworkeeDataTable zeigt sie in Ansichten passend zu ihren Werten.

Marken-Facette mit Anzahlen

Regeln pro Zeile

Manche Zeilen sind nicht für alle. Ein Entity-Filter schränkt Listen, Zähler, Facetten und Einzelabfragen gleichermaßen ein:

internal sealed class DocumentODataFilter(DocumentVisibility visibility) : IODataEntityFilter<Document>
{
    public Task<IQueryable<Document>> ApplyAsync(IQueryable<Document> query, CancellationToken cancellationToken) =>
        visibility.VisibleAsync(query, cancellationToken);
}

services.AddScoped<IODataEntityFilter<Document>, DocumentODataFilter>();

Eigenschaften ausblenden

Interne Felder bleiben aus dem Modell, sie werden weder ausgeliefert noch sind sie filterbar:

services.AddODataEntity<Document>("Documents", DocumentPermissions.Documents.View, d => d.BlobKey);

Aus dem Blazor-Client

IODataClient baut die URL und liest Werte, Anzahl und Facetten:

var page = await odata.QueryAsync<BrandDto>("Brands", new ODataQuery { OrderBy = "Name", Top = 1000, Count = false });

Der BFF leitet /odata mit dem Token des Benutzers an die API weiter.

Client-Entities

Markieren Sie eine Entity mit [ClientEntity], und Blazor-Clients halten eine Kopie ihres Sets und fragen sie lokal ab; siehe Client-Entities.

Excel-Export und -Import

Jedes Entity-Set lässt sich als Excel-Arbeitsmappe exportieren. Der Export nimmt dieselben $filter, $search und $orderby wie die Liste und beachtet Berechtigung, Entity-Filter und ausgeblendete Eigenschaften:

GET /api/v1/data/Products/export?$filter=Rate gt 10&$orderby=Name

Die Zeilenzahl begrenzt Coworkee:OData:MaxExportRows (Standard 100 000).

Ein Import macht aus jeder Zeile einer hochgeladenen Tabelle einen Command. Spalten werden per Name auf Eigenschaften abgebildet, unbekannte Spalten ignoriert. Jede Zeile läuft einzeln durch die Pipeline, Validierung und Berechtigungen gelten also; fehlerhafte Zeilen kommen mit ihrer Zeilennummer zurück:

// Zeilen sind der Request-Typ, das Lambda verpackt sie in den Command
services.AddODataImport("Brands", (AddEditBrandRequest row) => new AddEditBrandCommand(null, row));

// oder der Command selbst ist die Zeile
services.AddODataImport<AddNoteCommand>("Notes");
POST /api/v1/data/Brands/import   (multipart, Feld "file")
-> { "imported": 12, "errors": [ { "row": 7, "message": "Name: must not be empty" } ] }

Import-Ergebnis

CoworkeeDataTable bietet beides: Excel im Export-Menü und einen Import-Button mit Importable="true".