Skip to content

SyncfusionExamples/odatav4-adaptor-with-pivot-table

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

8 Commits
 
 
 
 
 
 

Repository files navigation

Syncfusion® React Pivot Table – OData V4 Adaptor Quick Start

A production-ready quick start that connects the Syncfusion® React Pivot Table to an ASP.NET Core OData V4 backend using the ODataV4Adaptor of the DataManager — enabling remote data binding, server-side query processing, and full CRUD operations over standard OData endpoints.

React .NET OData V4 Syncfusion License


📑 Table of Contents


🚀 Quick Overview

This project demonstrates how to bind the Syncfusion® React Pivot Table to a remote ASP.NET Core OData V4 service using the ODataV4Adaptor of the DataManager. The ODataV4Adaptor is purpose-built for REST endpoints that follow the OData V4 protocol, enabling standardized querying, filtering, sorting, paging, and CRUD operations.

Component Technology Purpose
🎨 Frontend React 19+ TypeScript (Vite) + Syncfusion® EJ2 Render the interactive Pivot Table UI (App.tsx)
⚙️ Backend ASP.NET Core 10.0 (OData 9.4) Serve OData V4 endpoints with [EnableQuery] support
🔌 Adaptor ODataV4Adaptor Bridge between Pivot Table and OData service over standard URLs
📊 Sample Data In-memory OrdersDetails list Simulate order records for the Pivot Table

💡 The ODataV4Adaptor is ideal when you want server-side query processing (filtering, sorting, paging) using a standardized open protocol — eliminating the need to write custom REST query conventions.


✨ Key Features

  • 📊 Remote Data Binding – Connects the Pivot Table to an OData V4 endpoint over HTTP.
  • 🧩 TypeScript Client – The React front-end (App.tsx, main.tsx) is fully typed; field names, edit settings, and event-handler arguments are validated at build time.
  • 🔄 Full CRUD Support – Insert, update, and delete records directly from the Pivot Table drill-through grid using POST, PATCH, and DELETE verbs.
  • 🎨 Standard OData Querying – All filter, sort, page, and aggregation logic is delegated to the server using $filter, $orderby, $skip, $top, and $count query options.
  • 🗂️ Standardized Response Format – Returns OData-compliant JSON with value and @odata.count for client-side parsing.
  • 🔑 Primary Key Configuration – Uses OrderID as the primary key (via the [Key] attribute) for unique record identification.
  • 🌐 CORS-Enabled – Preconfigured to allow cross-origin requests from the Vite dev server.
  • 🛡️ Property Casing Preservation – Uses DefaultContractResolver to keep PascalCase fields like OrderID, CustomerID.
  • Drill-Through Editing – Double-click a pivot cell to add, edit, or delete underlying records in a pop-up grid.
  • 📦 Ready-to-Run – Clone, build, and start both projects — no database setup required (in-memory sample data).

🛠️ Prerequisites

Make sure the following software and packages are installed on your machine before running the project.

Software / Package Version Purpose
🟢 Node.js 18.x or later Runtime for the Vite + React dev server
⚛️ React 19.x or later Build the Pivot Table client
🔷 TypeScript 5.x or later Type-checks the .tsx client source (App.tsx, main.tsx)
🟣 .NET SDK 10.0 or later Build and run the ASP.NET Core OData V4 service
🧑‍💻 Visual Studio / VS Code Latest Configure and run the backend API
📦 @syncfusion/ej2-react-pivotview Latest React Pivot Table component
📦 @syncfusion/ej2-data Latest DataManager and ODataV4Adaptor
📦 Microsoft.AspNetCore.OData 9.4.1+ OData V4 server-side support (query parsing, EDM, formatting)
📦 Microsoft.AspNetCore.Mvc.NewtonsoftJson 10.0.9+ Preserve original property casing during JSON serialization

📂 Project Structure

odatav4-adaptor-with-pivot-table/
├── 📁 Client/                              # React frontend (Pivot Table, TypeScript + Vite)
│   ├── 📄 index.html                       # Vite HTML entry point
│   ├── 📄 package.json                     # React dependencies & scripts
│   ├── 📄 tsconfig.json                    # TypeScript project references
│   ├── 📄 tsconfig.app.json                # TypeScript config for the app
│   ├── 📄 tsconfig.node.json               # TypeScript config for Vite/node tooling
│   ├── 📄 vite.config.ts                   # Vite configuration
│   ├── 📄 .oxlintrc.json                   # OxLint configuration
│   ├── 📁 public/
│   │   └── vite.svg
│   └── 📁 src/
│       ├── 📄 App.css                      # Component styles (Syncfusion Tailwind3 theme)
│       ├── 📄 App.tsx                      # Pivot Table with ODataV4Adaptor configuration (TypeScript)
│       ├── 📄 index.css                    # Global styles
│       ├── 📄 main.tsx                     # React entry point (renders <App/>)
│       └── 📁 assets/
│           └── react.svg
│
├── 📁 ODataV4Adaptor/                      # ASP.NET Core OData V4 backend
│   ├── 📁 Controllers/
│   │   └── OrdersController.cs             # OData endpoints: GET, GET(key), POST, PATCH(key), DELETE(key)
│   ├── 📁 Models/
│   │   └── OrdersDetails.cs                # Order data model + sample data
│   ├── 📁 Properties/
│   │   └── launchSettings.json             # Default ports: https 7181, http 5092
│   ├── appsettings.Development.json
│   ├── appsettings.json
│   ├── ODataV4Adaptor.csproj               # Project file (targets net10.0)
│   ├── ODataV4Adaptor.http                 # Endpoint testing file
│   └── Program.cs                          # OData + Newtonsoft.Json + CORS configuration
│
└── 📄 README.md                            # You are here

🏗️ Architecture & Data Flow

The application is split into two processes that communicate over HTTP using the OData V4 protocol.

┌──────────────────────────────────┐         ┌──────────────────────────────────────┐
│  React Client (localhost:5173)  │         │  ASP.NET Core OData V4 Backend       │
│  (Vite + TypeScript)             │         │  (http://localhost:5092)             │
│  ┌────────────────────────────┐  │         │                                      │
│  │  PivotViewComponent        │  │         │  ┌──────────────────────────────┐    │
│  │  (Syncfusion EJ2)          │  │         │  │ OrdersController : ODataCtrl│    │
│  │  src/App.tsx               │  │         │  │  • GET    /odata/Orders      │    │
│  └─────────────┬──────────────┘  │         │  │  • GET    /odata/Orders({k}) │    │
│                │                 │         │  │  • POST   /odata/Orders      │    │
│  ┌─────────────▼──────────────┐  │  HTTP   │  │  • PATCH  /odata/Orders({k}) │    │
│  │  DataManager               │──┼────────▶│  │  • DELETE /odata/Orders({k}) │    │
│  │  url: /odata/Orders         │  │  OData  │  └────────────┬─────────────────┘    │
│  │  adaptor: ODataV4Adaptor   │  │   V4    │               │                      │
│  └────────────────────────────┘  │         │  ┌────────────▼─────────────────┐    │
│                                  │         │  │ [EnableQuery]                │    │
│                                  │         │  │ OrdersDetails.GetAllRecords()│    │
│                                  │         │  │ (in-memory list)             │    │
│                                  │         │  └──────────────────────────────┘    │
└──────────────────────────────────┘         └──────────────────────────────────────┘

Step-by-step request flow (initial load):

  1. The React DataManager in App.tsx issues GET http://localhost:5092/odata/Orders to the backend.
  2. The request passes through the OData middleware registered in Program.cs.
  3. OrdersController.Get() returns OrdersDetails.GetAllRecords().AsQueryable() decorated with [EnableQuery].
  4. ASP.NET Core OData serializes the data into the OData-compliant envelope:
    {
      "value": [ { "OrderID": 10001, "...": "..." }, ... ],
      "@odata.count": 5
    }
  5. The ODataV4Adaptor parses the response and feeds the records into the Pivot Table.

Step-by-step request flow (CRUD from drill-through):

User action HTTP verb URL template Controller method
Double-click pivot cell GET /odata/Orders?... Get()
Add new record POST /odata/Orders Post(addRecord)
Edit existing record PATCH /odata/Orders({key}) Patch(key, updatedOrder)
Delete record DELETE /odata/Orders({key}) Delete(key)

🔑 The OrderID field is marked as the primary key by the [Key] attribute on the model and by the beginDrillThrough event in App.tsx. The ODataV4Adaptor uses this key when generating URLs for PATCH and DELETE.


⚙️ Installation & Setup

1. Clone the Repository

git clone https://github.com/SyncfusionExamples/odatav4-adaptor-with-pivot-table.git
cd odatav4-adaptor-with-pivot-table

2. Backend – ASP.NET Core OData V4 Service

The backend project lives in the ODataV4Adaptor/ folder.

2.1 Restore dependencies

cd ODataV4Adaptor
dotnet restore

2.2 Verify the required NuGet packages

Open ODataV4Adaptor.csproj and confirm the following package references are present:

<!-- filepath: ODataV4Adaptor/ODataV4Adaptor.csproj -->
<Project Sdk="Microsoft.NET.Sdk.Web">

  <PropertyGroup>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.AspNetCore.Mvc.NewtonsoftJson" Version="10.0.9" />
    <PackageReference Include="Microsoft.AspNetCore.OData" Version="9.4.1" />
    <PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="10.0.9" />
  </ItemGroup>

</Project>
  • Microsoft.AspNetCore.OData – Adds OData V4 query parsing, EDM model creation, and response formatting ($filter, $orderby, $top, $skip, $count).
  • Microsoft.AspNetCore.Mvc.NewtonsoftJson – Preserves PascalCase property names (OrderID, CustomerID) during JSON serialization.

If any are missing, add them with:

dotnet add package Microsoft.AspNetCore.OData --version 9.4.1
dotnet add package Microsoft.AspNetCore.Mvc.NewtonsoftJson --version 10.0.9

2.3 Inspect the configuration

Program.cs configures the OData service, JSON serialization, and CORS:

// filepath: ODataV4Adaptor/Program.cs
using Newtonsoft.Json.Serialization;
using Microsoft.OData.ModelBuilder;
using ODataV4Adaptor.Models;
using Microsoft.AspNetCore.OData;

var builder = WebApplication.CreateBuilder(args);

// Create an ODataConventionModelBuilder to build the OData model.
var modelBuilder = new ODataConventionModelBuilder();

// Register the "Pivot" entity set with the OData model builder.
// "Pivot" will be the name used in URLs (e.g., /odata/Orders).
modelBuilder.EntitySet<OrdersDetails>("Pivot");

builder.Services.AddMvc().AddNewtonsoftJson(options =>
{
  options.SerializerSettings.ContractResolver = new DefaultContractResolver();
});

builder.Services.AddCors(options =>
{
  options.AddDefaultPolicy(policy =>
  {
    policy.AllowAnyOrigin()
          .AllowAnyMethod()
          .AllowAnyHeader();
  });
});

builder.Services.AddControllers();

// Register OData services and route components under the "odata" prefix.
builder.Services.AddControllers().AddOData(
    options => options
        .Count()                                                  // Enables $count
        .AddRouteComponents("odata", modelBuilder.GetEdmModel()));// "odata" is the URL prefix

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.MapOpenApi();
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
app.UseCors();

app.Run();

🔒 Production CORS: Replace AllowAnyOrigin() with policy.WithOrigins("https://yourdomain.com").

2.4 Inspect the OData model

// filepath: ODataV4Adaptor/Models/OrdersDetails.cs
using System.ComponentModel.DataAnnotations;

namespace ODataV4Adaptor.Models
{
    public class OrdersDetails
    {
        public static List<OrdersDetails> order = new List<OrdersDetails>();

        public OrdersDetails() { }

        public OrdersDetails(int OrderID, string CustomerId, int EmployeeId, string ShipCountry)
        {
            this.OrderID = OrderID;
            this.CustomerID = CustomerId;
            this.EmployeeID = EmployeeId;
            this.ShipCountry = ShipCountry;
        }

        public static List<OrdersDetails> GetAllRecords()
        {
            if (order.Count() == 0)
            {
                int code = 10000;
                for (int i = 1; i < 2; i++)
                {
                    order.Add(new OrdersDetails(code + 1, "ALFKI", i + 0, "Denmark"));
                    order.Add(new OrdersDetails(code + 2, "ANATR", i + 2, "Brazil"));
                    code += 5;
                }
            }
            return order;
        }

        [Key]
        public int? OrderID { get; set; }       // Primary key — used for CRUD key handling
        public string? CustomerID { get; set; }
        public int? EmployeeID { get; set; }
        public string? ShipCountry { get; set; }
    }
}

ℹ️ The sample currently seeds only two records. Uncomment the additional order.Add(...) lines (or expand the loop) to populate more data for the Pivot Table.

2.5 Inspect the OData controller

// filepath: ODataV4Adaptor/Controllers/PivotController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.OData.Query;
using Microsoft.AspNetCore.OData.Routing.Controllers;
using ODataV4Adaptor.Models;

namespace ODataV4Adaptor.Server.Controllers
{
    [Route("[controller]")]
    [ApiController]
    public class PivotController : ODataController
    {
        // READ: Get all orders
        [HttpGet]
        [EnableQuery]
        public IActionResult Get()
        {
            var data = OrdersDetails.GetAllRecords().AsQueryable();
            return Ok(data);
        }

        // READ: Get single order by key
        [HttpGet("{key}")]
        [EnableQuery]
        public IActionResult Get(int key)
        {
            var order = OrdersDetails.GetAllRecords().FirstOrDefault(o => o.OrderID == key);
            if (order == null) return NotFound();
            return Ok(order);
        }

        // CREATE: Insert new order
        [HttpPost]
        [EnableQuery]
        public IActionResult Post([FromBody] OrdersDetails addRecord)
        {
            if (addRecord == null) return BadRequest("Order cannot be null");
            OrdersDetails.GetAllRecords().Insert(0, addRecord);
            return Created(addRecord);
        }

        // UPDATE: Modify existing order
        [HttpPatch("{key}")]
        public IActionResult Patch(int key, [FromBody] OrdersDetails updatedOrder)
        {
            if (updatedOrder == null) return BadRequest("Order data cannot be null");

            var existingOrder = OrdersDetails.GetAllRecords().FirstOrDefault(o => o.OrderID == key);
            if (existingOrder == null) return NotFound();

            existingOrder.CustomerID  = updatedOrder.CustomerID  ?? existingOrder.CustomerID;
            existingOrder.EmployeeID  = updatedOrder.EmployeeID  ?? existingOrder.EmployeeID;
            existingOrder.ShipCountry = updatedOrder.ShipCountry ?? existingOrder.ShipCountry;

            return Ok(existingOrder);
        }

        // DELETE: Remove order
        [HttpDelete("{key}")]
        public IActionResult Delete(int key)
        {
            var order = OrdersDetails.GetAllRecords().FirstOrDefault(o => o.OrderID == key);
            if (order == null) return NotFound();

            OrdersDetails.GetAllRecords().Remove(order);
            return NoContent();
        }
    }
}

The [EnableQuery] attribute is mandatory for OData query functionality. Without it, the server returns the full data set and ignores $filter, $orderby, $top, $skip, and $count.

3. Frontend – React Pivot Table

The React client lives in the Client/ folder. It is a TypeScript + Vite project built with .tsx files (App.tsx, main.tsx), so a TypeScript compiler (typescript) and the @types/react / @types/react-dom packages are listed under devDependencies in package.json. Vite serves the dev server and performs fast type-aware HMR.

3.1 Install npm dependencies

cd ../Client
npm install

3.2 Verify the API URL

Open src/App.tsx and ensure the url in the DataManager points to your backend port. The default in this repo is http://localhost:5092 (HTTP profile from launchSettings.json). Update the value if your backend is exposed on a different port.

// filepath: Client/src/App.tsx
import * as React from 'react';
import { PivotViewComponent, CellEditSettings } from '@syncfusion/ej2-react-pivotview';
import { DataManager, ODataV4Adaptor } from '@syncfusion/ej2-data';
import type { DataSourceSettingsModel } from '@syncfusion/ej2-pivotview/src/model/datasourcesettings-model';
import type { BeginDrillThroughEventArgs } from '@syncfusion/ej2-pivotview';
import './App.css';

function App(): React.ReactElement {
    // Configure DataManager with ODataV4Adaptor.
    const data: DataManager = new DataManager({
        url: 'http://localhost:5092/odata/Orders',   // 👈 Update this if your backend uses a different port
        adaptor: new ODataV4Adaptor(),
    });

    // Enable editing functionality
    const editSettings: CellEditSettings = {
        allowEditing: true,    // Enables the Edit button and allows users to modify existing records
        allowAdding: true,     // Enables the Add button and allows users to create new records
        allowDeleting: true,   // Enables the Delete button and allows users to remove records
        mode: 'Normal'         // Uses Normal mode (popup dialog) for editing; other options: 'Dialog', 'Batch'
    };

    const dataSourceSettings: DataSourceSettingsModel = {
        dataSource: data,
        expandAll: false,
        rows: [{ name: 'CustomerID' }],
        columns: [{ name: 'OrderID' }],
        values: [{ name: 'Freight' }],
        formatSettings: [{ name: 'Freight', format: 'N0' }],
    };

    const pivotObj = React.useRef<PivotViewComponent>(null);

    // Configure beginDrillThrough event to set the primary key for CRUD operations
    function beginDrillThrough(args: BeginDrillThroughEventArgs) {
        for (var i = 0; i < args.gridObj.columns.length; i++) {
            if (args.gridObj.columns[i].field === "OrderID") {
                // Mark this column as the primary key so DataManager uses it for CRUD key handling
                args.gridObj.columns[i].isPrimaryKey = true;
            } else {
                args.gridObj.columns[i].visible = true;
                if (args.gridObj.columns[i].field === 'OrderDate' || args.gridObj.columns[i].field === 'ShippedDate') {
                    args.gridObj.columns[i].editType = 'datetimepickeredit';
                }
            }
        }
    }

    return (
        <div className='control-section' style={{ margin: 100 }}>
            <PivotViewComponent
                ref={pivotObj}
                id='PivotView'
                height={350}
                width={700}
                editSettings={editSettings}
                beginDrillThrough={beginDrillThrough}
                dataSourceSettings={dataSourceSettings}>
            </PivotViewComponent>
        </div>
    );
}

export default App;

ℹ️ The .tsx version of the sample adds explicit type annotations:

  • data: DataManager on the data source,
  • dataSourceSettings: DataSourceSettingsModel (imported as a type from @syncfusion/ej2-pivotview/src/model/datasourcesettings-model),
  • editSettings: CellEditSettings from @syncfusion/ej2-react-pivotview,
  • pivotObj = React.useRef<PivotViewComponent>(null),
  • args: BeginDrillThroughEventArgs (imported as a type from @syncfusion/ej2-pivotview).

These let the TypeScript compiler catch misconfigured field names, edit options, and event handler arguments at build time.


▶️ Running the Application

You need two terminals — one for the backend OData service and one for the React client.

▶️ Start the Backend (Terminal 1)

cd ODataV4Adaptor
dotnet run

The API starts on the URLs defined in Properties/launchSettings.json:

  • 🔒 HTTPS: https://localhost:7181 (recommended)
  • 🌐 HTTP: http://localhost:5092

Verify it works:

  • 🌐 Open http://localhost:5092/odata/Orders in your browser.
  • ✅ You should see an OData-compliant JSON envelope:
    {
      "value": [
        { "OrderID": 10001, "CustomerID": "ALFKI", "EmployeeID": 1, "Freight": 10.0 },
        { "OrderID": 10002, "CustomerID": "ANATR", "EmployeeID": 3, "Freight": 20.0 },
        { "OrderID": 10003, "CustomerID": "ANTON", "EmployeeID": 2, "Freight": 30.0 },
        { "OrderID": 10004, "CustomerID": "BLONP", "EmployeeID": 4, "Freight": 40.0 },
        { "OrderID": 10005, "CustomerID": "BOLID", "EmployeeID": 5, "Freight": 50.0 }
      ],
      "@odata.count": 5
    }

📝 Note the port number in the terminal output and update the url in Client/src/App.tsx if it differs from http://localhost:5092.

▶️ Start the Frontend (Terminal 2)

cd Client
npm run dev

Vite serves the React dev server on http://localhost:5173 by default. The exact port is printed in the terminal output — paste it into the browser if Vite picks a different one. 🎉

You should see the Pivot Table populated with aggregated Freight values, grouped by CustomerID (rows) and OrderID (columns).

✅ Verify in the Browser

  1. Open the browser's Developer Tools (F12) → Network tab.
  2. Reload the page.
  3. You should see a GET http://localhost:5092/odata/Orders request with status 200 and an OData response containing value and @odata.count.
  4. The Pivot Table renders the aggregated data automatically.

🧪 Testing CRUD Operations

The Pivot Table supports full CRUD through its built-in drill-through editing grid.

Step Action Expected Network Request
1️⃣ Double-click any pivot cell to open the drill-through grid showing underlying source records. GET /odata/Orders
➕ 2️⃣ Click Add, fill in the new row fields, then click Update. POST http://localhost:5092/odata/Orders
✏️ 3️⃣ Click Edit on an existing row, change a field, then click Update. PATCH http://localhost:5092/odata/Orders({key})
🗑️ 4️⃣ Click Delete on a row to remove it. DELETE http://localhost:5092/odata/Orders({key})
🔁 5️⃣ The Pivot Table automatically refreshes to display the updated aggregated data from the backend. New GET /odata/Orders

🔑 The OrderID column is automatically marked as the primary key inside the beginDrillThrough event in App.tsx, so PATCH and DELETE operations know which record to target. On the server, the same key is identified by the [Key] attribute on OrdersDetails.OrderID.


🔍 OData Query Options Reference

The ODataV4Adaptor emits the standard OData V4 query options below. All of them are processed server-side by ASP.NET Core OData when the [EnableQuery] attribute is present.

Query option Purpose Example
$filter Filter records by a boolean expression /odata/Orders?$filter=ShipCountry eq 'Denmark'
$orderby Sort by one or more fields /odata/Orders?$orderby=OrderID desc
$top Take the first N records /odata/Orders?$top=5
$skip Skip the first N records /odata/Orders?$skip=10&$top=10
$count Include total record count /odata/Orders?$count=true
$select Project a subset of fields /odata/Orders?$select=OrderID,CustomerID
$expand Include related entities (not used in this sample) /odata/Orders?$expand=...

OData filter operators you can use with $filter:

Operator Meaning Example
eq equals ShipCountry eq 'Denmark'
ne not equals ShipCountry ne 'Denmark'
gt greater than OrderID gt 10005
lt less than OrderID lt 10010
and logical AND ShipCountry eq 'Denmark' and OrderID gt 10005
or logical OR ShipCountry eq 'Denmark' or ShipCountry eq 'Germany'

Querying features that benefit the Pivot Table:

  • Filtering – The Pivot Table sends $filter whenever the user applies filters via the field list or filter dialog.
  • Sorting – Sort headers (e.g., OrderID desc) are translated to $orderby.
  • Paging / virtualization – Page-based requests are translated to $skip and $top. The @odata.count field is required for accurate page counts (enabled by .Count() in Program.cs).
  • Aggregation – The Pivot Table aggregates client-side from the records returned by OData, so it works seamlessly over the standard value array.
  • Grouping – The Pivot Table composes group keys from the field values; the server returns all records and the client groups/aggregates them.

ℹ️ The current Program.cs only enables .Count(). If you need the server to handle $filter, $orderby, $top, and $skip directly (which [EnableQuery] already does), no additional changes are required. To restrict available query capabilities, chain .Select(), .Filter(), .OrderBy(), .Expand(), or .SetMaxTop(...) in the OData registration.


🔧 Troubleshooting

❓ Issue 🔍 Symptom ✅ Resolution
🚫 Empty Pivot Table Pivot loads with no errors but no rows or values appear. Ensure the API returns OData-compliant JSON ({ value, @odata.count }) and that field names match dataSourceSettings.
404 Not Found Network tab shows a 404 response when the Pivot Table loads. Confirm the backend is running, the entity set is named Orders (registered via modelBuilder.EntitySet<OrdersDetails>("Orders")), and the URL in App.tsx matches http://localhost:5092/odata/Orders.
💥 500 Internal Server Error The Pivot Table fails and the browser shows a server error. Check the terminal/Visual Studio output for stack traces. Common causes: missing [EnableQuery], null body, or model issues.
🌐 CORS Blocked Console shows Access to XMLHttpRequest ... has been blocked by CORS policy. Verify CORS is configured in Program.cs and app.UseCors() is called.
💾 CRUD operations not saving The edit dialog closes but changes are not reflected in the data. Confirm OrderID is the primary key ([Key] attribute + isPrimaryKey in beginDrillThrough).
🔤 Property casing mismatch Pivot appears empty or shows "field not found" even though the API returns data. Ensure DefaultContractResolver is registered in Program.cs so PascalCase fields like OrderID are preserved.
📉 Only 5 records visible Pivot shows just the five seeded records from the single-iteration loop. Increase the loop bound in OrdersDetails.GetAllRecords() (for example, change for (int i = 1; i < 2; i++) to i < 9) to seed more sample data for the Pivot Table.
🔒 SSL/TLS certificate error Console shows net::ERR_CERT_AUTHORITY_INVALID or browser warns about an untrusted certificate. Run dotnet dev-certs https --clean then dotnet dev-certs https --trust. Or switch to the http profile (port 5092).
❌ Missing OData package Build error: "The type or namespace name 'OData' could not be found". Install Microsoft.AspNetCore.OData 9.4.1 via NuGet.
🔁 [EnableQuery] ignored The server returns the full set even when $filter is sent. Ensure the [EnableQuery] attribute is applied to the controller method and the OData route is registered in Program.cs.

📖 API Reference

The backend exposes the following OData V4 endpoints through PivotController. All endpoints follow standard OData conventions; query options like $filter, $orderby, $top, $skip, and $count are processed by the framework when [EnableQuery] is present.

Method Route Purpose Request Body Response
GET /odata/Orders Retrieve all order records (with OData query) { value: [...], @odata.count }
GET /odata/Orders({key}) Retrieve a single order by primary key Single OrdersDetails object
POST /odata/Orders Insert a new order OrdersDetails JSON 201 Created
PATCH /odata/Orders({key}) Update an existing order (matched by OrderID) OrdersDetails JSON 200 OK
DELETE /odata/Orders({key}) Remove an order by primary key 204 No Content

The OrdersDetails model exposes the following fields:

Field Type Description
OrderID int? Unique order identifier (primary key)
CustomerID string? Identifier of the customer
EmployeeID int? Identifier of the handling employee
ShipCountry string? Destination country

ℹ️ The model currently exposes only four fields. To bind richer Pivot Table configurations (e.g., ShipCity, Freight, OrderDate, ShipName, ShippedDate, ShipAddress, Verified) as referenced in the Pivot Table configuration, add the corresponding properties to OrdersDetails and re-seed sample data in GetAllRecords(). The datasource.js file in the React client contains a richer local-data reference but is not used by the OData service in this sample.


🤝 Contributing

Contributions are welcome and appreciated! 💖

  1. 🍴 Fork the repository.
  2. 🌿 Create a feature branch: git checkout -b feature/my-awesome-change
  3. 💾 Commit your changes: git commit -m "Add my awesome change"
  4. 📤 Push to your branch: git push origin feature/my-awesome-change
  5. 🔁 Open a Pull Request describing the change and its motivation.

📋 Contribution Guidelines

  • Follow the existing code style in both the React and ASP.NET Core projects.
  • Keep changes focused — one feature or fix per pull request.
  • Update or add documentation (README.md, odatav4-adaptor.md) when behavior changes.
  • Test your changes locally against both the backend and frontend before submitting.

📜 License & Support

📄 License

This project is released under the MIT License. You are free to use, modify, and distribute the code in personal and commercial projects. See the LICENSE file for full text.

🛟 Support

⭐ If this project helped you, please consider giving it a star on GitHub — it helps others discover it!


📚 Related Resources


Built with ❤️ using React, .NET and the OData V4 standard by the Syncfusion® team.

About

A quick start project that demonstrates how to bind remote data from an OData V4 service to the Syncfusion React Pivot Table using the ODataV4Adaptor with an ASP.NET Core backend.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors