|
| 1 | +--- |
| 2 | +layout: post |
| 3 | +title: Data Restructuring with Blazor TreeGrid and AI Models | Syncfusion |
| 4 | +description: Learn how to use Syncfusion Blazor TreeGrid with AI service to automatically organize hierarchical data. |
| 5 | +platform: Blazor |
| 6 | +control: AI Integration |
| 7 | +documentation: ug |
| 8 | +keywords: Blazor TreeGrid, AI data restructuring, Syncfusion Blazor AI |
| 9 | +--- |
| 10 | + |
| 11 | +# Data Restructuring with Blazor TreeGrid and Ollama |
| 12 | + |
| 13 | +This guide demonstrates how to use the [**Syncfusion.Blazor.AI**](https://www.nuget.org/packages/Syncfusion.Blazor.AI) package to automatically organize hierarchical data in a Syncfusion Blazor TreeGrid component. The [**Syncfusion.Blazor.AI**](https://www.nuget.org/packages/Syncfusion.Blazor.AI) package enables integration with AI models to process and structure data, while Ollama provides self-hosted or cloud-based AI capabilities for analyzing relationships in datasets. In this example, the application assigns appropriate `ParentId` values based on `CategoryName` relationships, dynamically updating the TreeGrid to reflect the corrected hierarchical structure. |
| 14 | + |
| 15 | +## Prerequisites |
| 16 | + |
| 17 | +Ensure the following NuGet packages are installed based on your chosen AI service: |
| 18 | + |
| 19 | +### For OpenAI |
| 20 | +- **Microsoft.Extensions.AI** |
| 21 | +- **Microsoft.Extensions.AI.OpenAI** |
| 22 | + |
| 23 | +### For Azure OpenAI |
| 24 | +- **Microsoft.Extensions.AI** |
| 25 | +- **Microsoft.Extensions.AI.OpenAI** |
| 26 | +- **Azure.AI.OpenAI** |
| 27 | + |
| 28 | +### For Ollama |
| 29 | +- **Microsoft.Extensions.AI** |
| 30 | +- **OllamaSharp** |
| 31 | + |
| 32 | +{% tabs %} |
| 33 | +{% highlight C# tabtitle="Package Manager" %} |
| 34 | + |
| 35 | +Install-Package Syncfusion.Blazor.TreeGrid -Version {{ site.releaseversion }} |
| 36 | +Install-Package Syncfusion.Blazor.Themes -Version {{ site.releaseversion }} |
| 37 | +Install-Package Syncfusion.Blazor.AI -Version {{ site.releaseversion }} |
| 38 | +Install-Package Microsoft.Extensions.AI |
| 39 | +Install-Package Microsoft.Extensions.AI.OpenAI # For OpenAI or Azure OpenAI |
| 40 | +Install-Package Azure.AI.OpenAI # For Azure OpenAI |
| 41 | +Install-Package OllamaSharp # For Ollama |
| 42 | + |
| 43 | +{% endhighlight %} |
| 44 | +{% endtabs %} |
| 45 | + |
| 46 | +## Add Stylesheet and Script Resources |
| 47 | + |
| 48 | +Include the theme stylesheet and script from NuGet via [Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets) in the `<head>` of your main page: |
| 49 | + |
| 50 | +- For **.NET 6** Blazor Server apps, add to **~/Pages/_Layout.cshtml**. |
| 51 | +- For **.NET 8 or .NET 9** Blazor Server apps, add to **~/Components/App.razor**. |
| 52 | + |
| 53 | +```html |
| 54 | +<head> |
| 55 | + <link href="_content/Syncfusion.Blazor.Themes/tailwind.css" rel="stylesheet" /> |
| 56 | +</head> |
| 57 | +<body> |
| 58 | + <script src="_content/Syncfusion.Blazor.Core/scripts/syncfusion-blazor.min.js" type="text/javascript"></script> |
| 59 | +</body> |
| 60 | +``` |
| 61 | + |
| 62 | +N> Explore the [Blazor Themes](https://blazor.syncfusion.com/documentation/appearance/themes) topic for methods to reference themes ([Static Web Assets](https://blazor.syncfusion.com/documentation/appearance/themes#static-web-assets), [CDN](https://blazor.syncfusion.com/documentation/appearance/themes#cdn-reference), or [CRG](https://blazor.syncfusion.com/documentation/common/custom-resource-generator)). Refer to the [Adding Script Reference](https://blazor.syncfusion.com/documentation/common/adding-script-references) topic for different approaches to adding script references in your Blazor application. |
| 63 | + |
| 64 | +## Configure AI Service |
| 65 | + |
| 66 | +Choose one of the following AI services (OpenAI, Azure OpenAI, or Ollama) based on your requirements: |
| 67 | +- **OpenAI**: Best for cloud-based, general-purpose AI models with minimal setup. |
| 68 | +- **Azure OpenAI**: Ideal for enterprise-grade deployments with enhanced security and scalability. |
| 69 | +- **Ollama**: Suitable for self-hosted, privacy-focused AI models. |
| 70 | + |
| 71 | +Follow the instructions for your selected service to register the AI model in your application. |
| 72 | + |
| 73 | +### OpenAI |
| 74 | + |
| 75 | +Generate an API key from OpenAI and set `openAIApiKey`. Specify the desired model (e.g., `gpt-3.5-turbo`, `gpt-4`) in `openAIModel`. |
| 76 | + |
| 77 | +- Install the required NuGet packages: |
| 78 | + |
| 79 | +{% tabs %} |
| 80 | +{% highlight C# tabtitle="Package Manager" %} |
| 81 | + |
| 82 | +Install-Package Microsoft.Extensions.AI |
| 83 | +Install-Package Microsoft.Extensions.AI.OpenAI |
| 84 | + |
| 85 | +{% endhighlight %} |
| 86 | +{% endtabs %} |
| 87 | + |
| 88 | +- Add the following to the **~/Program.cs** file in your Blazor WebApp: |
| 89 | + |
| 90 | +{% tabs %} |
| 91 | +{% highlight C# tabtitle="Blazor WebApp" hl_lines="7 8 9 11 12 13" %} |
| 92 | + |
| 93 | +using Syncfusion.Blazor.AI; |
| 94 | +using Microsoft.Extensions.AI; |
| 95 | +using OpenAI; |
| 96 | + |
| 97 | +var builder = WebApplication.CreateBuilder(args); |
| 98 | + |
| 99 | +string openAIApiKey = "API-KEY"; |
| 100 | +string openAIModel = "OPENAI_MODEL"; |
| 101 | +OpenAIClient openAIClient = new OpenAIClient(openAIApiKey); |
| 102 | +IChatClient openAIChatClient = openAIClient.GetChatClient(openAIModel).AsIChatClient(); |
| 103 | +builder.Services.AddChatClient(openAIChatClient); |
| 104 | +builder.Services.AddSingleton<IChatInferenceService, SyncfusionAIService>(); |
| 105 | + |
| 106 | +var app = builder.Build(); |
| 107 | + |
| 108 | +{% endhighlight %} |
| 109 | +{% endtabs %} |
| 110 | + |
| 111 | +### Azure OpenAI |
| 112 | + |
| 113 | +Deploy an Azure OpenAI Service resource and model as described in [Microsoft's documentation](https://learn.microsoft.com/en-us/azure/ai-services/openai/how-to/create-resource). Obtain values for `azureOpenAIKey`, `azureOpenAIEndpoint`, and `azureOpenAIModel`. |
| 114 | + |
| 115 | +- Install the required NuGet packages: |
| 116 | + |
| 117 | +{% tabs %} |
| 118 | +{% highlight C# tabtitle="Package Manager" %} |
| 119 | + |
| 120 | +Install-Package Microsoft.Extensions.AI |
| 121 | +Install-Package Microsoft.Extensions.AI.OpenAI |
| 122 | +Install-Package Azure.AI.OpenAI |
| 123 | + |
| 124 | +{% endhighlight %} |
| 125 | +{% endtabs %} |
| 126 | + |
| 127 | +- Add the following to the **~/Program.cs** file in your Blazor WebApp: |
| 128 | + |
| 129 | +{% tabs %} |
| 130 | +{% highlight C# tabtitle="Blazor WebApp" hl_lines="7 8 9 11 12 13" %} |
| 131 | + |
| 132 | +using Syncfusion.Blazor.AI; |
| 133 | +using Azure.AI.OpenAI; |
| 134 | +using Microsoft.Extensions.AI; |
| 135 | +using System.ClientModel; |
| 136 | + |
| 137 | +var builder = WebApplication.CreateBuilder(args); |
| 138 | + |
| 139 | +string azureOpenAIKey = "AZURE_OPENAI_KEY"; |
| 140 | +string azureOpenAIEndpoint = "AZURE_OPENAI_ENDPOINT"; |
| 141 | +string azureOpenAIModel = "AZURE_OPENAI_MODEL"; |
| 142 | +AzureOpenAIClient azureOpenAIClient = new AzureOpenAIClient( |
| 143 | + new Uri(azureOpenAIEndpoint), |
| 144 | + new ApiKeyCredential(azureOpenAIKey) |
| 145 | +); |
| 146 | +IChatClient azureOpenAIChatClient = azureOpenAIClient.GetChatClient(azureOpenAIModel).AsIChatClient(); |
| 147 | +builder.Services.AddChatClient(azureOpenAIChatClient); |
| 148 | +builder.Services.AddSingleton<IChatInferenceService, SyncfusionAIService>(); |
| 149 | + |
| 150 | +var app = builder.Build(); |
| 151 | + |
| 152 | +{% endhighlight %} |
| 153 | +{% endtabs %} |
| 154 | + |
| 155 | +### Ollama |
| 156 | + |
| 157 | +To use Ollama for self-hosted AI models: |
| 158 | + |
| 159 | +1. **Download and install Ollama**: Visit [Ollama's official website](https://ollama.com) and install the application for your operating system. |
| 160 | +2. **Install a model**: Choose a model from the [Ollama Library](https://ollama.com/library) (e.g., `llama2:13b`, `mistral:7b`). |
| 161 | +3. **Configure the application**: Provide the `Endpoint` URL (e.g., `http://localhost:11434`) and `ModelName` (e.g., `llama2:13b`). |
| 162 | + |
| 163 | +- Install the required NuGet packages: |
| 164 | + |
| 165 | +{% tabs %} |
| 166 | +{% highlight C# tabtitle="Package Manager" %} |
| 167 | + |
| 168 | +Install-Package Microsoft.Extensions.AI |
| 169 | +Install-Package OllamaSharp |
| 170 | + |
| 171 | +{% endhighlight %} |
| 172 | +{% endtabs %} |
| 173 | + |
| 174 | +- Add the following to the **~/Program.cs** file in your Blazor WebApp: |
| 175 | + |
| 176 | +{% tabs %} |
| 177 | +{% highlight C# tabtitle="Blazor WebApp" hl_lines="7 8 9 11 12 13" %} |
| 178 | + |
| 179 | +using Syncfusion.Blazor.AI; |
| 180 | +using Microsoft.Extensions.AI; |
| 181 | +using OllamaSharp; |
| 182 | + |
| 183 | +var builder = WebApplication.CreateBuilder(args); |
| 184 | + |
| 185 | +string ModelName = "MODEL_NAME"; |
| 186 | +IChatClient chatClient = new OllamaApiClient("http://localhost:11434", ModelName); |
| 187 | +builder.Services.AddChatClient(chatClient); |
| 188 | +builder.Services.AddSingleton<IChatInferenceService, SyncfusionAIService>(); |
| 189 | + |
| 190 | +var app = builder.Build(); |
| 191 | + |
| 192 | +{% endhighlight %} |
| 193 | +{% endtabs %} |
| 194 | + |
| 195 | +- **Verify connectivity**: Ensure the Ollama server is running and accessible at the specified endpoint (e.g., `http://localhost:11434`) before starting the application. |
| 196 | + |
| 197 | +## Register Syncfusion Blazor Service |
| 198 | + |
| 199 | +Add the Syncfusion Blazor service to your **~/Program.cs** file. The configuration depends on your app's **Interactive Render Mode**: |
| 200 | + |
| 201 | +- **Server Mode**: Register the service in the single **~/Program.cs** file. |
| 202 | +- **WebAssembly or Auto Mode**: Register the service in both the server-side **~/Program.cs** and client-side **~/Program.cs** files. |
| 203 | + |
| 204 | +{% tabs %} |
| 205 | +{% highlight C# tabtitle="Server (~/_Program.cs)" hl_lines="3 11" %} |
| 206 | + |
| 207 | +using Syncfusion.Blazor; |
| 208 | + |
| 209 | +var builder = WebApplication.CreateBuilder(args); |
| 210 | + |
| 211 | +builder.Services.AddRazorComponents() |
| 212 | + .AddInteractiveServerComponents() |
| 213 | + .AddInteractiveWebAssemblyComponents(); |
| 214 | +builder.Services.AddSyncfusionBlazor(); |
| 215 | + |
| 216 | +var app = builder.Build(); |
| 217 | + |
| 218 | +{% endhighlight %} |
| 219 | +{% highlight C# tabtitle="Client (~/_Program.cs)" hl_lines="2 5" %} |
| 220 | + |
| 221 | +using Syncfusion.Blazor; |
| 222 | + |
| 223 | +var builder = WebAssemblyHostBuilder.CreateDefault(args); |
| 224 | +builder.Services.AddSyncfusionBlazor(); |
| 225 | + |
| 226 | +await builder.Build().RunAsync(); |
| 227 | + |
| 228 | +{% endhighlight %} |
| 229 | +{% endtabs %} |
| 230 | + |
| 231 | +## Razor Component (`Home.razor`) |
| 232 | + |
| 233 | +This section implements the Syncfusion Blazor TreeGrid with AI-driven data restructuring using the AI model to assign `ParentId` values based on `CategoryName` relationships. |
| 234 | + |
| 235 | +```csharp |
| 236 | +@page "/" |
| 237 | + |
| 238 | +@inject IChatInferenceService AIService |
| 239 | +@using Syncfusion.Blazor.TreeGrid |
| 240 | +@using Syncfusion.Blazor.Navigations |
| 241 | +@using Syncfusion.Blazor.Buttons |
| 242 | +@using Syncfusion.Blazor.AI |
| 243 | +@using System.Text.Json; |
| 244 | + |
| 245 | +<div style="padding-bottom: 10px;"> |
| 246 | + <span>@message</span> |
| 247 | +</div> |
| 248 | +<div style="width: 100%; position: relative"> |
| 249 | + <SfTreeGrid @ref="TreeGrid" DataSource="@TreeGridData" IdMapping="CategoryId" ParentIdMapping="ParentId" TreeColumnIndex="1"> |
| 250 | + <TreeGridEditSettings AllowEditing="true" /> |
| 251 | + <TreeGridColumns> |
| 252 | + <TreeGridColumn Field="CategoryId" HeaderText="Category ID" IsPrimaryKey="true" Width="60" TextAlign="Syncfusion.Blazor.Grids.TextAlign.Right"></TreeGridColumn> |
| 253 | + <TreeGridColumn Field="CategoryName" HeaderText="Category Name" Width="100"></TreeGridColumn> |
| 254 | + <TreeGridColumn Field="Status" HeaderText="Status" Width="70"></TreeGridColumn> |
| 255 | + <TreeGridColumn Field="OrderDate" HeaderText="Last Order Date" Format="d" Width="90"></TreeGridColumn> |
| 256 | + </TreeGridColumns> |
| 257 | + <SfToolbar ID="TreeGrid_AISample_Toolbar"> |
| 258 | + <ToolbarItems> |
| 259 | + <ToolbarItem> |
| 260 | + <Template> |
| 261 | + <SfButton IsPrimary ID="openAI" @onclick="OpenAIHandler">Smart Data Restructure</SfButton> |
| 262 | + </Template> |
| 263 | + </ToolbarItem> |
| 264 | + </ToolbarItems> |
| 265 | + </SfToolbar> |
| 266 | + </SfTreeGrid> |
| 267 | +</div> |
| 268 | +``` |
| 269 | + |
| 270 | +`Home.razor.cs` |
| 271 | +```csharp |
| 272 | +using Microsoft.Extensions.AI; |
| 273 | +using Syncfusion.Blazor.AI; |
| 274 | +using Syncfusion.Blazor.TreeGrid; |
| 275 | +using System.Text.Json; |
| 276 | + |
| 277 | +namespace AISamples.Components.Pages |
| 278 | +{ |
| 279 | + public partial class Home |
| 280 | + { |
| 281 | + public SfTreeGrid<TreeData.BusinessObject> TreeGrid; |
| 282 | + private string AIPrompt = string.Empty; |
| 283 | + private string message = string.Empty; |
| 284 | + public List<TreeData.BusinessObject> TreeGridData { get; set; } |
| 285 | + protected override void OnInitialized() |
| 286 | + { |
| 287 | + this.TreeGridData = TreeData.GetAdaptiveStructureData().ToList(); |
| 288 | + } |
| 289 | + |
| 290 | + private async Task OpenAIHandler() |
| 291 | + { |
| 292 | + await TreeGrid.ShowSpinnerAsync(); |
| 293 | + List<TreeData.BusinessObject> sortedCollection = new List<TreeData.BusinessObject>(); |
| 294 | + var AIPrompt = GeneratePrompt(TreeGridData); |
| 295 | + ChatParameters chatParameters = new ChatParameters |
| 296 | + { |
| 297 | + Messages = new List<ChatMessage> |
| 298 | + { |
| 299 | + new ChatMessage(ChatRole.User, AIPrompt) |
| 300 | + } |
| 301 | + }; |
| 302 | + var result = await AIService.GenerateResponseAsync(chatParameters); |
| 303 | + result = result.Replace("```json", "").Replace("```", "").Trim(); |
| 304 | + |
| 305 | + string response = JsonDocument.Parse(result).RootElement.GetProperty("TreeGridData").ToString(); |
| 306 | + if (response is not null) |
| 307 | + { |
| 308 | + sortedCollection = JsonSerializer.Deserialize<List<TreeData.BusinessObject>>(response); |
| 309 | + } |
| 310 | + if (sortedCollection is not null && sortedCollection.Count > 0) |
| 311 | + { |
| 312 | + TreeGridData = sortedCollection.Cast<TreeData.BusinessObject>().ToList(); |
| 313 | + } |
| 314 | + else |
| 315 | + { |
| 316 | + message = "Oops.! Please try Again !"; |
| 317 | + } |
| 318 | + await TreeGrid.HideSpinnerAsync(); |
| 319 | + await Task.CompletedTask; |
| 320 | + } |
| 321 | + |
| 322 | + private string GeneratePrompt(List<TreeData.BusinessObject> TreeGridData) |
| 323 | + { |
| 324 | + Dictionary<string, IEnumerable<object>> treeData = new Dictionary<string, IEnumerable<object>>(); |
| 325 | + treeData.Add("TreeGridData", TreeGridData); |
| 326 | + var jsonData = JsonSerializer.Serialize(treeData); |
| 327 | + return @"I want you to act as a TreeGrid Data Organizer. |
| 328 | + Your task is to organize a dataset based on a hierarchical structure using 'CategoryId' and 'ParentId'. |
| 329 | + Each item in the dataset has a 'CategoryName' representing categories, and some categories have a null 'ParentId', indicating they are top-level categories. |
| 330 | + Your role will be to examine the entire dataset to identify related items based on their 'CategoryName' values and nest them under the appropriate top-level categories by updating their 'ParentId' to match the 'CategoryId' of the corresponding top-level category. |
| 331 | + For example, if a category like 'Furniture' exists, you should examine the dataset for items such as 'Chair' and 'Table' and update their 'ParentId' to the 'CategoryId' of 'Furniture'. |
| 332 | + The output should be the newly prepared TreeGridData with correctly assigned 'ParentId' values. Please ensure that all subcategories are correctly nested under their respective top-level categories. |
| 333 | + Return the newly prepared TreeGridData alone and don't share any other information with the response: Here is the dataset " + jsonData + "/n Note: Return response must be in json string and with no other explanation. "; |
| 334 | + } |
| 335 | + public class TreeData |
| 336 | + { |
| 337 | + public class BusinessObject |
| 338 | + { |
| 339 | + public int CategoryId { get; set; } |
| 340 | + public string CategoryName { get; set; } |
| 341 | + public string Status { get; set; } |
| 342 | + public DateTime OrderDate { get; set; } |
| 343 | + public int? ParentId { get; set; } |
| 344 | + } |
| 345 | + |
| 346 | + public static List<BusinessObject> GetAdaptiveStructureData() |
| 347 | + { |
| 348 | + List<BusinessObject> BusinessObjectCollection = new List<BusinessObject>(); |
| 349 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 1, CategoryName = "Electronics", Status = "Available", OrderDate = new DateTime(2021, 7, 12), ParentId = null }); |
| 350 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 2, CategoryName = "Cell phone", Status = "out of Stock", OrderDate = new DateTime(2021, 6, 17), ParentId = 1 }); |
| 351 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 3, CategoryName = "Computer", Status = "Available", OrderDate = new DateTime(2021, 7, 12), ParentId = 7 }); |
| 352 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 4, CategoryName = "Cloth", Status = "Available", OrderDate = new DateTime(2021, 10, 5), ParentId = null }); |
| 353 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 5, CategoryName = "Silk", Status = "Out of Stock", OrderDate = new DateTime(2021, 9, 2), ParentId = 7 }); |
| 354 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 6, CategoryName = "Chair", Status = "Available", OrderDate = new DateTime(2021, 3, 3), ParentId = 1 }); |
| 355 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 7, CategoryName = "Furniture", Status = "Available", OrderDate = new DateTime(2021, 3, 5), ParentId = null }); |
| 356 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 8, CategoryName = "Bed", Status = "Available", OrderDate = new DateTime(2021, 3, 5), ParentId = 7 }); |
| 357 | + BusinessObjectCollection.Add(new BusinessObject() { CategoryId = 9, CategoryName = "Fabrics", Status = "Available", OrderDate = new DateTime(2021, 10, 5), ParentId = 4 }); |
| 358 | + return BusinessObjectCollection; |
| 359 | + } |
| 360 | + } |
| 361 | + } |
| 362 | +} |
| 363 | +``` |
| 364 | + |
| 365 | +## Error Handling and Troubleshooting |
| 366 | + |
| 367 | +If the AI service fails to return a valid response, the TreeGrid will display an error message ("Oops! Please try again!"). Common issues include: |
| 368 | + |
| 369 | +- **Invalid API Key or Endpoint**: Verify that the `openAIApiKey`, `azureOpenAIKey`, or Ollama `Endpoint` is correct and the service is accessible. |
| 370 | +- **Model Unavailable**: Ensure the specified `openAIModel`, `azureOpenAIModel`, or `ModelName` is deployed and supported. |
| 371 | +- **Network Issues**: Check connectivity to the AI service endpoint, especially for self-hosted Ollama instances. |
| 372 | +- **Large Datasets**: Processing large datasets may cause timeouts. Consider batching data or optimizing the prompt for efficiency. |
| 373 | + |
| 374 | +## Performance Considerations |
| 375 | + |
| 376 | +When handling large datasets, ensure the Ollama server has sufficient resources (CPU/GPU) to process requests efficiently. For datasets exceeding 10,000 records, consider splitting the data into smaller batches to avoid performance bottlenecks. Test the application with your specific dataset to determine optimal performance. |
| 377 | + |
| 378 | +## Sample Code |
| 379 | + |
| 380 | +A complete working example is available in the [Syncfusion Blazor AI Samples GitHub repository](https://github.com/syncfusion/smart-ai-samples). |
| 381 | + |
| 382 | + |
0 commit comments