diff --git a/blazor-toc.html b/blazor-toc.html index 1151ca716d..421865c20f 100644 --- a/blazor-toc.html +++ b/blazor-toc.html @@ -3142,6 +3142,9 @@ + +
  • + Resource Allocation
  • Resource Allocation @@ -4300,7 +4303,12 @@
  • Appointment customization
  • -
  • Data Binding
  • +
  • + Data Binding + +
  • CRUD Actions
  • Virtual Scrolling
  • Editor Window Customization
  • diff --git a/blazor/datagrid/connecting-to-adaptors/graphql-adaptor.md b/blazor/datagrid/connecting-to-adaptors/graphql-adaptor.md index 17535fe12f..d62204aa54 100644 --- a/blazor/datagrid/connecting-to-adaptors/graphql-adaptor.md +++ b/blazor/datagrid/connecting-to-adaptors/graphql-adaptor.md @@ -1,169 +1,309 @@ --- layout: post -title: Bind GraphQL Data in Blazor DataGrid | Syncfusion -description: Learn how to bind data from a GraphQL API to the Syncfusion Blazor DataGrid, including querying, mutation, and integration techniques. -platform: Blazor +title: Blazor Data Grid with GraphQL: CRUD & Data Operations | Syncfusion +description: Build Blazor apps with GraphQL and Syncfusion Data Grid to enable CRUD, filtering, sorting, paging, and grouping for seamless data operations. control: DataGrid +platform: blazor documentation: ug --- -# GraphQL Adaptor in Blazor DataGrid +# Connect Syncfusion Blazor DataGrid with GraphQL using Hot Chocolate -GraphQL is a powerful query language for APIs, designed to provide a more efficient alternative to traditional REST APIs. It allows you to precisely fetch the data you need, reducing over-fetching and under-fetching of data. GraphQL provides a flexible and expressive syntax for querying, enabling clients to request only the specific data they require. +GraphQL is a query language that allows applications to request exactly the data needed, nothing more and nothing less. Unlike traditional REST APIs that return fixed data structures, GraphQL enables the client to specify the shape and content of the response. -Syncfusion’s Blazor DataGrid seamlessly integrates with GraphQL servers using the GraphQLAdaptor in the [SfDataManager](https://blazor.syncfusion.com/documentation/data/getting-started-with-web-app). This specialized adaptor simplifies the interaction between the Grid and GraphQL servers, allowing efficient data retrieval with support for various operations like CRUD (Create, Read, Update and Delete), paging, sorting, and filtering. +**Traditional REST APIs** and **GraphQL** differ mainly in how data is requested and returned: **REST APIs expose** multiple endpoints that return fixed data structures, often including unnecessary fields and requiring several requests to fetch related data, while **GraphQL** uses a single endpoint where queries define the exact fields needed, enabling precise responses and allowing related data to be retrieved efficiently in one request. This makes **GraphQL** especially useful for **Blazor DataGrid integration**, the **reason** is data‑centric UI components require well‑structured and selective datasets to support efficient filtering, reduce network calls, and improve overall performance. -This section describes a step-by-step process for retrieving data from a GraphQL service using GraphQLAdaptor, then binding it to the Grid to facilitate data and CRUD operations. +**Key GraphQL Concepts** -## Configure a GraphQL server +- **Queries**: A query is a request to read data. Queries do not modify data; they only retrieve it. +- **Mutations**: A mutation is a request to modify data. Mutations create, update, or delete records. +- **Resolvers**: Each query or mutation is handled by a resolver, which is a function responsible for fetching data or executing an operation. **Query resolvers** handle **read operations**, while **mutation resolvers** handle **write operations**. +- **Schema**: Defines the structure of the API. The schema describes available data types, the fields within those types, and the operations that can be executed. Query definitions specify how data can be retrieved, and mutation definitions specify how data can be modified. -To configure a GraphQL server using Hot Chocolate with the Syncfusion® Blazor DataGrid, follow these steps: +[Hot Chocolate](https://chillicream.com/docs/hotchocolate/v15) is an open‑source GraphQL server framework for .NET. Hot Chocolate enables the creation of GraphQL APIs using ASP.NET Core and integrates seamlessly with modern .NET applications, including Blazor. -**Step 1: Create a new ASP.NET Core application** +## Prerequisites -- Open Visual Studio and select **Create a new project**. -- Choose **ASP.NET Core Web App** and name the project `GraphQLServer`. -- Alternatively, you can use a terminal or command prompt to create the project: - ```bash - dotnet new web -n GraphQLServer - ``` -- Navigate to the project directory: - ```bash - cd GraphQLServer - ``` +Install the following software and packages before starting the process: -**Step 2: Add the Hot Chocolate NuGet package** +| Software/Package | Version | Purpose | +|-----------------|---------|---------| +| Visual Studio 2022 | 17.0 or later | Development IDE with Blazor workload | +| .NET SDK | net8.0 or compatible | Runtime and build tools | +| HotChocolate.AspNetCore | 15.1 or later | GraphQL server framework | +| Syncfusion.Blazor.Grids | {{site.blazorversion}} | DataGrid component | +| Syncfusion.Blazor.Themes | {{site.blazorversion}} | Styling for DataGrid | -- Open the **NuGet Package Manager** by right-clicking on the project in the **Solution Explorer** and selecting **Manage NuGet Packages**. -- Go to the **Browse** tab, search for `HotChocolate.AspNetCore`, and select the package. -- Click **Install** to add it to your project. +## Setting Up the GraphQL Backend -Alternatively, you can use the **Package Manager Console** to install the package by running the following command: +### Step 1: Install Required NuGet Packages and Configure Launch Settings + +Before installing NuGet packages, a new Blazor Web Application must be created using the default template. The template automatically generates essential starter files—such as **Program.cs, appsettings.json, launchSettings.json, the wwwroot folder, and the Components folder**. + +For this guide, a Blazor application named **Grid_GraphQLAdaptor** has been created. + +**Install NuGet Packages** + +NuGet packages are software libraries that add functionality to applications. The following packages enable GraphQL server functionality+ and Syncfusion DataGrid components. + +**Required Packages:** + +- **HotChocolate.AspNetCore** (version 15.1 or later) - GraphQL server framework +- **Syncfusion.Blazor.Grids** (version {{site.blazorversion}}) - DataGrid component +- **Syncfusion.Blazor.Themes** (version {{site.blazorversion}}) - Styling for DataGrid + +**Method 1: Using Package Manager Console** + +1. Open Visual Studio 2022. +2. Navigate to **Tools → NuGet Package Manager → Package Manager Console**. +3. Run the following commands: ```powershell -Install-Package HotChocolate.AspNetCore +Install-Package HotChocolate.AspNetCore -Version 15.1.12 +Install-Package Syncfusion.Blazor.Grids -Version {{site.blazorversion}} +Install-Package Syncfusion.Blazor.Themes -Version {{site.blazorversion}} ``` -**Step 3: Create a model class** +**Method 2: Using NuGet Package Manager UI** -Add a new folder named **Models**. Then, add a model class named **OrderData.cs** in the **Models** folder to represent the order data. +1. Open **Visual Studio 2022 → Tools → NuGet Package Manager → Manage NuGet Packages for Solution**. +2. Search for and install each package individually: + - **HotChocolate.AspNetCore** (version 15.1.12 or later) + - **Syncfusion.Blazor.Grids** (version {{site.blazorversion}}) + - **Syncfusion.Blazor.Themes** (version {{site.blazorversion}}) -{% tabs %} -{% highlight cs tabtitle="OrderData.cs" %} +All required packages are now installed. -using System.Text.Json.Serialization; +--- -namespace GraphQLServer.Models -{ - public class OrderData - { - public static List Orders = new List(); +### Step 2: Register Hot Chocolate Services in Program.cs - public OrderData() { } +The `Program.cs` file configures and registers the GraphQL services. - public OrderData( - int orderID, string customerId, int employeeId, double freight, bool verified, - DateTime orderDate, string shipCity, string shipName, string shipCountry, - DateTime shippedDate, string shipAddress) - { - OrderID = orderID; - CustomerID = customerId; - EmployeeID = employeeId; - Freight = freight; - Verified = verified; - OrderDate = orderDate; - ShipCity = shipCity; - ShipName = shipName; - ShipCountry = shipCountry; - ShippedDate = shippedDate; - ShipAddress = shipAddress; - } +**Instructions:** - public static List GetAllRecords() - { - if (Orders.Count == 0) - { - int code = 10000; - for (int i = 1; i < 10; i++) - { - Orders.Add(new OrderData(code + 1, "ALFKI", i, 2.3 * i, false, new DateTime(1991, 05, 15), "Berlin", "Simons Bistro", "Denmark", new DateTime(1996, 7, 16), "Kirchgasse 6")); - Orders.Add(new OrderData(code + 2, "ANATR", i + 2, 3.3 * i, true, new DateTime(1990, 04, 04), "Madrid", "Queen Cozinha", "Brazil", new DateTime(1996, 9, 11), "Avda. Azteca 123")); - Orders.Add(new OrderData(code + 3, "ANTON", i + 1, 4.3 * i, true, new DateTime(1957, 11, 30), "Cholchester", "Frankenversand", "Germany", new DateTime(1996, 10, 7), "Carrera 52 con Ave. Bolívar #65-98 Llano Largo")); - Orders.Add(new OrderData(code + 4, "BLONP", i + 3, 5.3 * i, false, new DateTime(1930, 10, 22), "Marseille", "Ernst Handel", "Austria", new DateTime(1996, 12, 30), "Magazinweg 7")); - Orders.Add(new OrderData(code + 5, "BOLID", i + 4, 6.3 * i, true, new DateTime(1953, 02, 18), "Tsawassen", "Hanari Carnes", "Switzerland", new DateTime(1997, 12, 3), "1029 - 12th Ave. S.")); - code += 5; - } - } - return Orders; - } +1. Open the `Program.cs` file at the project root. +2. Add the following code after `var builder = WebApplication.CreateBuilder(args);`: + +```csharp +[Program.cs] + +using Grid_GraphQLAdaptor.Models; +using HotChocolate.Execution.Configuration; + +var builder = WebApplication.CreateBuilder(args); + +// Register Hot Chocolate GraphQL services +builder.Services + .AddGraphQLServer() + .AddQueryType() + .AddMutationType(); + +var app = builder.Build(); + +// Map the GraphQL endpoint (default: /graphql) +app.MapGraphQL(); + +app.Run(); +``` + +**Details:** - [JsonPropertyName("orderID")] - public int OrderID { get; set; } +- `AddGraphQLServer()` - Initializes the Hot Chocolate GraphQL server +- `AddQueryType()` - Registers query resolvers for read operations +- `AddMutationType()` - Registers mutation resolvers for write operations +- `MapGraphQL()` - Exposes the GraphQL endpoint at `/graphql` - [JsonPropertyName("customerID")] - public string? CustomerID { get; set; } +The GraphQL backend is now configured and ready. The GraphQL endpoint is accessible at `https://localhost:xxxx/graphql`. - [JsonPropertyName("employeeID")] - public int? EmployeeID { get; set; } +--- + +### Step 3: Configure Launch Settings (Port Configuration) + +The **launchsettings.json** file controls the port number where the application runs. This file is located in the **Properties** folder at **Properties/launchsettings.json**. + +**Instructions to Change the Port:** + +1. Open the **Properties** folder in the project root. +2. Double-click **launchsettings.json** to open the file. +3. Locate the `https` profile section: + +```json +"https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7001;http://localhost:5001", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } +} +``` - [JsonPropertyName("freight")] - public double? Freight { get; set; } +4. Modify the `applicationUrl` property to change the port numbers: + - `https://localhost:7001` - HTTPS port (change 7001 to desired port) + - `http://localhost:5001` - HTTP port (change 5001 to desired port) + +5. Example configuration with custom ports: + +```json +"https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7777;http://localhost:5555", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } +} +``` + +6. Save the file and restart the application for the changes to take effect. + +**Important Notes:** + +- Port numbers must be between 1024 and 65535. +- Avoid using ports already in use by other applications. +- The GraphQL endpoint will be accessible at the configured HTTPS URL (e.g., `https://localhost:7777/graphql`). + +All configuration steps are now complete. + +--- - [JsonPropertyName("verified")] - public bool? Verified { get; set; } +### Step 4: Create the Data Model - [JsonPropertyName("orderDate")] - public DateTime? OrderDate { get; set; } +A data model represents the structure of data that the application stores. It defines the properties (fields) that make up a record. Each property corresponds to a column in the database table. The data model acts as the blueprint for how data is organized and accessed throughout the application. - [JsonPropertyName("shipCity")] - public string ShipCity { get; set; } +In the context of an expense tracker, the data model defines what information is stored for each expense entry. Properties include the expense identifier, employee details, department, category, and financial amounts. - [JsonPropertyName("shipName")] - public string? ShipName { get; set; } +**Instructions**: - [JsonPropertyName("shipCountry")] - public string ShipCountry { get; set; } +1. Create a new folder named **Models** in the project root directory. +2. Inside the **Models** folder, create a new file named **ExpenseRecord.cs**. +3. Define the **ExpenseRecord** class with the following code: - [JsonPropertyName("shippedDate")] - public DateTime? ShippedDate { get; set; } +**File Location:** `Models/ExpenseRecord.cs` - [JsonPropertyName("shipAddress")] - public string? ShipAddress { get; set; } +```csharp +namespace Grid_GraphQLAdaptor.Models +{ + /// + /// Represents a single expense record stored in the database. + /// Each property corresponds to a database column. + /// + public class ExpenseRecord + { + /// + /// Unique identifier for each expense record. + /// This is the primary key in the database. + /// + public string ExpenseId { get; set; } + + /// + /// Name of the employee who submitted the expense. + /// + public string EmployeeName { get; set; } + + /// + /// Email address of the employee. + /// + public string EmployeeEmail { get; set; } + + /// + /// Department to which the employee belongs. + /// + public string Department { get; set; } + + /// + /// Category of the expense (e.g., Travel, Meals, Office Supplies). + /// + public string Category { get; set; } + + /// + /// Base amount of the expense before tax. + /// + public decimal Amount { get; set; } + + /// + /// Tax percentage applied to the expense. + /// + public decimal TaxPct { get; set; } + + /// + /// Total amount including tax (calculated: Amount + (Amount * TaxPct / 100)). + /// + public decimal TotalAmount { get; set; } } } +``` +**Property Mapping Reference** -{% endhighlight %} -{% endtabs %} +The following table shows how C# properties map to database columns and GraphQL field names: -**Step 4: Define the GraphQL query** +| Property Name (C#) | Database Column | GraphQL Field Name | Data Type | Purpose | +|---|---|---|---|---| +| `ExpenseId` | `expense_id` | `expenseId` | `String` | Unique identifier for the expense record | +| `EmployeeName` | `employee_name` | `employeeName` | `String` | Name of the employee submitting the expense | +| `EmployeeEmail` | `employee_email` | `employeeEmail` | `String` | Email address for contact purposes | +| `Department` | `department` | `department` | `String` | Organizational department | +| `Category` | `category` | `category` | `String` | Type or classification of expense | +| `Amount` | `amount` | `amount` | `Decimal` | Base expense amount before tax | +| `TaxPct` | `tax_pct` | `taxPct` | `Decimal` | Tax percentage applied | +| `TotalAmount` | `total_amount` | `totalAmount` | `Decimal` | Final amount including tax | -Create a `GraphQLQuery` class to define the query resolver for fetching order data. This class will handle the logic for retrieving data from the `OrderData` model. The following code demonstrates the `DataManagerRequestInput` class, which is passed as an argument to the resolver function. +**Important Convention: Camel Case Conversion** -{% tabs %} -{% highlight cs tabtitle="GraphQLQuery.cs" %} +**Hot Chocolate GraphQL** automatically converts C# property names (**PascalCase**) to GraphQL field names (**camelCase**). This convention ensures consistent naming in the GraphQL schema: -using GraphQLServer.Models; +- C# Property: `EmployeeName` → GraphQL Field: `employeeName` +- C# Property: `ExpenseId` → GraphQL Field: `expenseId` +- C# Property: `TotalAmount` → GraphQL Field: `totalAmount` + +**Explanation**: + +- The [Key] attribute marks the `ExpenseId` property as the primary key (a unique identifier for each record). +- Each property represents a column in the database table. +- The model provides the data structure that GraphQL uses to process queries and mutations. + +The expense data model has been successfully created. + +--- + +### Step 5: GraphQL Query Resolvers + +A query resolver is a method in the backend that handles read requests from the client. When the Blazor DataGrid needs to fetch data, it sends a GraphQL query to the server. The query resolver receives this request, processes it, and returns the appropriate data. Query resolvers do not modify data; they only retrieve and return it. + +In simple terms, a **GraphQL query** asks a question, +and a **resolver** is the one who answers it. + +**Instructions:** + +1. Inside the **Models** folder, create a new file named **GraphQLQuery.cs**. +2. Add the following code to define the query resolver: + +```csharp +[Models/GraphQLQuery.cs] + +using Grid_GraphQLAdaptor.Models; -/// -/// Represents the GraphQL query resolver for fetching order data. -/// public class GraphQLQuery { /// - /// Retrieves all order data and returns it along with the total record count. + /// Query resolver for fetching expense record data with data operations support. /// - /// The data manager request input containing query parameters. - /// An instance of containing the order data and count. - public OrdersDataResponse GetOrdersData(DataManagerRequestInput dataManager) + public ExpenseRecordDataResponse GetExpenseRecordData(DataManagerRequestInput dataManager) { - // Retrieve all order records from the data source. - List dataSource = OrderData.GetAllRecords(); + // Retrieve all expense records from the data source. + List dataSource = ExpenseRecord.GetAllRecords(); + + // Apply search, filter, sort, and paging operations as provided by the DataGrid. + // Operations are applied sequentially: search → filter → sort → paging. - // Calculate the total number of records in the data source. + // Store the total count before paging. int totalRecords = dataSource.Count; - // Return the response containing the total count and the data records. - return new OrdersDataResponse + // Return response with total count and paginated data. + return new ExpenseRecordDataResponse { Count = totalRecords, Result = dataSource @@ -172,1629 +312,2045 @@ public class GraphQLQuery } /// -/// Represents the response structure for order data queries. +/// Response structure for query results. Must include Count (total records) and Result (current page). /// -public class OrdersDataResponse +public class ExpenseRecordDataResponse { - /// - /// Gets or sets the total count of records. - /// public int Count { get; set; } - - /// - /// Gets or sets the list of order data records. - /// - public List Result { get; set; } = new List(); + public List Result { get; set; } = new List(); } +``` -{% endhighlight %} - -{% highlight cs tabtitle="DataManagerRequest.cs" %} - -namespace GraphQLServer.Models -{ - /// - /// Represents the input structure for data manager requests. - /// - public class DataManagerRequestInput - { - [GraphQLName("Skip")] - public int Skip { get; set; } +**Details:** - [GraphQLName("Take")] - public int Take { get; set; } +- The `GetExpenseRecordData` method receives `DataManagerRequestInput`, which contains filter, sort, search, and paging parameters from the DataGrid +- Hot Chocolate automatically converts the method name `GetExpenseRecordData` to camelCase: `expenseRecordData` in the GraphQL schema +- The response must contain `Count` (total records) and `Result` (current page data) for the DataGrid to process pagination - [GraphQLName("RequiresCounts")] - public bool RequiresCounts { get; set; } = false; +The query resolver has been created successfully. - [GraphQLName("Params")] - [GraphQLType(typeof(AnyType))] - public IDictionary Params { get; set; } +--- - [GraphQLName("Aggregates")] - [GraphQLType(typeof(AnyType))] - public List? Aggregates { get; set; } +### Step 6: Create the DataManagerRequestInput Class - [GraphQLName("Search")] - public List? Search { get; set; } +A **DataManagerRequestInput** class is a GraphQL input type that represents all the parameters the Syncfusion Blazor DataGrid sends to the backend when requesting data. This class acts as a container for filtering, sorting, searching, paging, and other data operation parameters. - [GraphQLName("Sorted")] - public List? Sorted { get; set; } +**Purpose** +When the DataGrid performs operations like pagination, sorting, filtering, or searching, it packages all these parameters into a `DataManagerRequestInput` object and sends it to the GraphQL backend. The backend then uses these parameters to fetch and return only the data the grid needs. - [GraphQLName("Where")] - [GraphQLType(typeof(AnyType))] - public List? Where { get; set; } +**Instructions**: - [GraphQLName("Group")] - public List? Group { get; set; } +1. Inside the **Models** folder, create a new file named **DataManagerRequestInput.cs**. +2. Define the **DataManagerRequestInput** class and supporting classes with the following code: - [GraphQLName("antiForgery")] - public string? antiForgery { get; set; } +```csharp +namespace Grid_GraphQLAdaptor.Models; - [GraphQLName("Table")] - public string? Table { get; set; } +/// +/// Represents the input structure for data manager requests from the Syncfusion Blazor DataGrid. +/// Contains all parameters needed for data operations like filtering, sorting, paging, and searching. +/// +public class DataManagerRequestInput +{ + /// + /// Number of records to skip from the beginning (used for pagination). + /// Example: Skip=10 means start from the 11th record. + /// + [GraphQLName("Skip")] + public int Skip { get; set; } - [GraphQLName("IdMapping")] - public string? IdMapping { get; set; } + /// + /// Number of records to retrieve (page size). + /// Example: Take=10 means retrieve 10 records per page. + /// + [GraphQLName("Take")] + public int Take { get; set; } - [GraphQLName("Select")] - public List? Select { get; set; } + /// + /// Indicates whether the total record count should be calculated. + /// Set to true when pagination requires knowing the total number of records. + /// + [GraphQLName("RequiresCounts")] + public bool RequiresCounts { get; set; } = false; - [GraphQLName("Expand")] - public List? Expand { get; set; } + /// + /// Search criteria for finding specific records. + /// Contains the search text and which fields to search in. + /// + [GraphQLName("Search")] + public List? Search { get; set; } - [GraphQLName("Distinct")] - public List? Distinct { get; set; } + // Add other parameters - [GraphQLName("ServerSideGroup")] - public bool? ServerSideGroup { get; set; } +} - [GraphQLName("LazyLoad")] - public bool? LazyLoad { get; set; } +/// +/// Represents an aggregate operation (Sum, Average, Min, Max, Count). +/// Used to calculate aggregate values on specified fields. +/// +public class Aggregate +{ + /// + /// Field name to aggregate (e.g., "Amount", "TotalAmount"). + /// + [GraphQLName("Field")] + public string? Field { get; set; } - [GraphQLName("LazyExpandAllGroup")] - public bool? LazyExpandAllGroup { get; set; } - } + /// + /// Type of aggregation: Sum, Average, Min, Max, Count. + /// + [GraphQLName("Type")] + public string? Type { get; set; } +} +/// +/// Represents search criteria for finding records. +/// Allows searching across multiple fields with specified operators. +/// +public class SearchFilter +{ /// - /// Represents an aggregate operation in the data manager request. + /// Fields to search in (e.g., ["EmployeeName", "Department"]). /// - public class Aggregate - { - [GraphQLName("Field")] - public string Field { get; set; } + [GraphQLName("Fields")] + public List? Fields { get; set; } - [GraphQLName("Type")] - public string Type { get; set; } - } + /// + /// The search keyword entered by the user. + /// + [GraphQLName("Key")] + public string? Key { get; set; } /// - /// Represents a search filter in the data manager request. + /// Comparison operator (contains, equals, startsWith, etc.). /// - public class SearchFilter - { - [GraphQLName("Fields")] - public List Fields { get; set; } + [GraphQLName("Operator")] + public string? Operator { get; set; } - [GraphQLName("Key")] - public string Key { get; set; } + /// + /// Whether the search should ignore case (case-insensitive search). + /// + [GraphQLName("IgnoreCase")] + public bool IgnoreCase { get; set; } - [GraphQLName("Operator")] - public string Operator { get; set; } + /// + /// Indicates whether to ignore accent marks and diacritic characters during search operations + /// + [GraphQLName("IgnoreAccent")] + public bool IgnoreAccent { get; set; } +} - [GraphQLName("IgnoreCase")] - public bool IgnoreCase { get; set; } - } +/// +/// Represents sorting instructions for ordering records. +/// Defines which field to sort by and in which direction. +/// +public class Sort +{ + /// + /// Field name to sort by (e.g., "Amount", "EmployeeName"). + /// + [GraphQLName("Name")] + public string? Name { get; set; } /// - /// Represents a sorting operation in the data manager request. + /// Sort direction: "Ascending" or "Descending". /// - public class Sort - { - [GraphQLName("Name")] - public string Name { get; set; } + [GraphQLName("Direction")] + public string? Direction { get; set; } - [GraphQLName("Direction")] - public string Direction { get; set; } + // Add other properties +} - [GraphQLName("Comparer")] - [GraphQLType(typeof(AnyType))] - public object Comparer { get; set; } - } +/// +/// Represents a filter condition for narrowing down records. +/// Supports complex nested conditions (AND/OR logic) for advanced filtering. +/// +public class WhereFilter +{ + /// + /// Field name to filter by (e.g., "Department", "Amount"). + /// + [GraphQLName("Field")] + public string? Field { get; set; } /// - /// Represents a filter condition in the data manager request. + /// Ignore case sensitivity in comparisons. /// - public class WhereFilter - { - [GraphQLName("Field")] - public string? Field { get; set; } + [GraphQLName("IgnoreCase")] + public bool? IgnoreCase { get; set; } - [GraphQLName("IgnoreCase")] - public bool? IgnoreCase { get; set; } + // Add other properties +} - [GraphQLName("IgnoreAccent")] - public bool? IgnoreAccent { get; set; } +// Add other classes - [GraphQLName("IsComplex")] - public bool? IsComplex { get; set; } +``` - [GraphQLName("Operator")] - public string? Operator { get; set; } +**Understanding the DataManagerRequestInput Class** - [GraphQLName("Condition")] - public string? Condition { get; set; } +**Example Scenario:** A sequence of operations is performed on the DataGrid as follows: - [GraphQLName("Value")] - [GraphQLType(typeof(AnyType))] - public object? Value { get; set; } +- Searches for **"Finance"** in the Department column. +- Filters for amounts greater than 1000. +- Sorts by Amount in descending order. +- Navigates to page 2 (showing records 11-20). +- Resulting **DataManagerRequestInput** Parameters: - [GraphQLName("predicates")] - public List? predicates { get; set; } +```json +{ + "dataManager": { + "Skip": 10, + "Take": 10, + "RequiresCounts": true, + "Search": [ + { + "Fields": ["Department"], + "Key": "Finance", + "Operator": "contains", + "IgnoreCase": true, + "IgnoreAccent": true + } + ], + "Where": [ + { + "Condition": "and", + "Predicates": [ + { + "Field": "Amount", + "Operator": "greaterThan", + "Value": 1000, + "Predicates": [] + } + ] + } + ], + "Sorted": [ + { + "Name": "Amount", + "Direction": "Descending" + } + ] } } +``` -{% endhighlight %} -{% endtabs %} - -**Step 5: Configure the GraphQL server** -Update the `Program.cs` file to configure the GraphQL server. This configuration ensures that the server can handle GraphQL requests effectively. +**DataManagerRequestInput Properties:** -{% tabs %} -{% highlight cs tabtitle="Program.cs" %} +| Property | Purpose | Type | Example | +|----------|---------|------|---------| +| `Skip` | Number of records to skip (used for pagination) | `int` | `10` (skip first 10 records) | +| `Take` | Number of records to retrieve per page | `int` | `20` (fetch next 20 records) | +| `Search` | Search filter configuration | `List` | Search term and target fields | +| `Where` | Filter conditions for column filtering | `List` | Field name, operator, and value | +| `Sorted` | Sort specifications for ordering records | `List` | Field name and direction (asc/desc) | +| `Group` | Grouping configuration | `List` | Field names to group by | -var builder = WebApplication.CreateBuilder(args); +**Key Attributes Explained** +[GraphQLName]: Maps C# property names to GraphQL schema field names. **Hot Chocolate** automatically converts PascalCase to camelCase. -// Register GraphQL services. -builder.Services.AddGraphQLServer() - .AddQueryType(); +Example: **RequiresCounts → requiresCounts** +[GraphQLType(typeof(AnyType))]: Allows flexible typing for complex nested structures that can contain various data types. -var app = builder.Build(); +### Step 7: GraphQL Mutation Resolvers -// Use routing middleware. -app.UseRouting(); +A **GraphQL mutation resolver** is a method in the backend that handles write requests (data modifications) from the client. While queries only read data, mutations create, update, or delete records. When the Blazor DataGrid performs add, edit, or delete operations, it sends a GraphQL mutation to the server. The mutation resolver receives this request, processes it, and persists the changes to the data source. -// Map endpoints. -app.MapGet("/", () => "Hello, World!"); -app.MapGraphQL(); // Maps the /graphql endpoint by default. +In simple terms, a **GraphQL mutation** asks for a change, and a **resolver** is the one who makes it. -app.Run(); +**Instructions:** +1. Inside the Models folder, create a new file named **GraphQLMutation.cs**. +2. Define the **GraphQLMutation** class with the following code: -{% endhighlight %} -{% endtabs %} +```csharp +using Grid_GraphQLAdaptor.Models; +using HotChocolate.Types; -**Step 6: Test the GraphQL endpoint** +namespace Grid_GraphQLAdaptor.Models +{ + /// + /// GraphQL Mutation class that handles all write operations (Create, Update, Delete). + /// Each method is a resolver that processes data modification requests from the DataGrid. + /// + public class GraphQLMutation + { + /// + /// Mutation resolver for creating a new expense record. + /// Called when a user clicks the "Add" button in the DataGrid and submits a new record. + /// + public ExpenseRecord CreateExpense( + ExpenseRecord record, + int index, + string action, + [GraphQLType(typeof(AnyType))] IDictionary additionalParameters) + { + // Add logic to create new expense record + return record; + } -To verify that the GraphQL server is functioning correctly, use the following example query in a GraphQL client or playground: + /// + /// Mutation resolver for updating an existing expense record. + /// Called when a user clicks the "Edit" button, modifies values, and submits the changes. + /// + public ExpenseRecord UpdateExpense( + ExpenseRecord record, + string action, + string primaryColumnName, + string primaryColumnValue, + [GraphQLType(typeof(AnyType))] IDictionary additionalParameters) + { + // Add logic to update existing expense record + + return record; + } -``` -{ - ordersData { - count - result { - orderID - customerID - shipCity - shipCountry + /// + /// Mutation resolver for deleting an expense record. + /// Called when a user clicks the "Delete" button and confirms the deletion. + /// + public bool DeleteExpense( + string primaryColumnValue, + [GraphQLType(typeof(AnyType))] IDictionary additionalParameters) + { + // Add logic to delete existing expense record + + return true; } } } ``` -This query will return the total count of orders and a list of order details. Ensure the server is running and accessible at `http://localhost:xxxx/graphql` before testing. Here, `xxxx` represents the port number. +A mutation resolver is a C# method decorated with GraphQL attributes that: -For more details, refer to the [Hot Chocolate documentation](https://chillicream.com/docs/hotchocolate). +- **Receives input parameters** from the DataGrid (record data, primary keys, etc.). +- **Processes the operation** (validation, calculation, data modification). +- **Persists changes** to the data source (database, file, memory). +- **Returns results** to the client (modified record or success/failure status). -## Connecting Syncfusion® Blazor DataGrid to an GraphQL service - -To integrate the Syncfusion® Blazor DataGrid into your project using Visual Studio, follow the below steps: +The GraphQL Mutation class has been successfully created and is ready to handle all data modification operations from the Syncfusion Blazor DataGrid. -**Step 1: Create a Blazor Web App** +--- + +## Integrating Syncfusion Blazor DataGrid -Create a **Blazor Web App** named **BlazorGrid** using Visual Studio 2022. You can use either [Microsoft Templates](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0) or the [Syncfusion® Blazor Extension](https://blazor.syncfusion.com/documentation/visual-studio-integration/template-studio). Ensure you configure the appropriate [interactive render mode](https://learn.microsoft.com/en-us/aspnet/core/blazor/components/render-modes?view=aspnetcore-8.0#render-modes) and [interactivity location](https://learn.microsoft.com/en-us/aspnet/core/blazor/tooling?view=aspnetcore-8.0&pivots=windows). +### Step 1: Install and Configure Blazor DataGrid Components with GraphQL -**Step 2: Install Syncfusion® Blazor DataGrid and Themes NuGet packages** +Syncfusion is a library that provides pre-built UI components like DataGrid, which is used to display data in a table format. -To add the Syncfusion® Blazor DataGrid to your app, open the NuGet Package Manager in Visual Studio (*Tools → NuGet Package Manager → Manage NuGet Packages for Solution*). Search for and install the following packages: +**Instructions:** -- [Syncfusion.Blazor.Grid](https://www.nuget.org/packages/Syncfusion.Blazor.Grid/) -- [Syncfusion.Blazor.Themes](https://www.nuget.org/packages/Syncfusion.Blazor.Themes/) +1. The Syncfusion.Blazor.Grids package was installed in [**Step 1**](#step-1-install-and-configure-blazor-datagrid-components-with-graphql) of the previous heading. +2. Import the required namespaces in the `Components/_Imports.razor` file: -If your Blazor Web App uses `WebAssembly` or `Auto` render modes, install these packages in the client project. +```csharp +@using Syncfusion.Blazor.Grids +@using Syncfusion.Blazor.Data +``` -Alternatively, use the following Package Manager commands: +3. Add the Syncfusion stylesheet and scripts in the `Components/App.razor` file. Find the `` section and add: -```powershell -Install-Package Syncfusion.Blazor.Grid -Version {{ site.releaseversion }} -Install-Package Syncfusion.Blazor.Themes -Version {{ site.releaseversion }} +```html + + + + + ``` +For this project, the tailwind3 theme is used. A different theme can be selected or the existing theme can be customized based on project requirements. Refer to the [Syncfusion Blazor Components Appearance](https://sfblazor.azurewebsites.net/staging/documentation/appearance/themes) documentation to learn more about theming and customization options. -> Syncfusion® Blazor components are available on [nuget.org](https://www.nuget.org/packages?q=syncfusion.blazor). Refer to the [NuGet packages](https://blazor.syncfusion.com/documentation/nuget-packages) topic for a complete list of available packages. +Syncfusion components are now configured and ready to use. For additional guidance, refer to the Grid component’s [getting‑started](https://blazor.syncfusion.com/documentation/datagrid/getting-started-with-web-app) documentation. -**Step 3: Register Syncfusion® Blazor service** +### Step 2: Update the Blazor DataGrid -- Open the **~/_Imports.razor** file and import the required namespaces: +The `Home.razor` component will display the expense data in a Syncfusion Blazor DataGrid with search, filter, sort, editing and pagination capabilities. -```cs -@using Syncfusion.Blazor -@using Syncfusion.Blazor.Grids -@using Syncfusion.Blazor.Data -``` +**Instructions:** -- Register the Syncfusion® Blazor service in the **~/Program.cs** file: +1. Open the file named `Home.razor` in the `Components/Pages` folder. +2. Add the following code to create a basic DataGrid: -```csharp -using Syncfusion.Blazor; +```cshtml +@page "/" +@rendermode InteractiveServer + +Expense Tracker System + +
    +
    +

    Expense Tracker System

    +

    Manage and view all expenses from the database.

    + + + + + + + + + + + + +
    +
    -builder.Services.AddSyncfusionBlazor(); +@code { + // GraphQLAdaptorOptions will be added in the next step +} ``` -For apps using `WebAssembly` or `Auto (Server and WebAssembly)` render modes, register the service in both **~/Program.cs** files. +**Component Explanation:** -**Step 4: Add stylesheet and script resources** +- **`@rendermode InteractiveServer`**: Enables interactive server-side rendering for the component. +- **``**: The DataGrid component that displays data in rows and columns. +- **``**: Defines individual columns in the DataGrid. +- **``**: Configures pagination with 10 records per page. +- **``**: Enable editing functionality directly within the Grid by setting the AllowEditing, AllowAdding, and AllowDeleting properties within the GridEditSettings to **true**. +- **`