{"id":24151347,"url":"https://github.com/xaviersolau/tablemodel","last_synced_at":"2025-09-08T02:38:26.380Z","repository":{"id":44947941,"uuid":"410670761","full_name":"xaviersolau/TableModel","owner":"xaviersolau","description":"Library providing a way to define table model and to apply paging sorting and filtering operation server side.","archived":false,"fork":false,"pushed_at":"2024-12-16T21:22:08.000Z","size":401,"stargazers_count":7,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-08-21T11:03:24.837Z","etag":null,"topics":["blazor","database","dotnet","http-requests","table","virtualization"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/xaviersolau.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-26T21:52:20.000Z","updated_at":"2025-05-02T09:20:00.000Z","dependencies_parsed_at":"2023-01-20T04:03:17.644Z","dependency_job_id":null,"html_url":"https://github.com/xaviersolau/TableModel","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/xaviersolau/TableModel","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviersolau%2FTableModel","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviersolau%2FTableModel/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviersolau%2FTableModel/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviersolau%2FTableModel/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/xaviersolau","download_url":"https://codeload.github.com/xaviersolau/TableModel/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/xaviersolau%2FTableModel/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274124885,"owners_count":25226430,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["blazor","database","dotnet","http-requests","table","virtualization"],"created_at":"2025-01-12T09:15:14.280Z","updated_at":"2025-09-08T02:38:26.355Z","avatar_url":"https://github.com/xaviersolau.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"﻿# TableModel\nLibrary providing a way to define table model and to apply paging sorting and filtering operation\non server side.\n\nDon't hesitate to post issues, pull requests on the project or to fork and improve the project.\n\n## Project dashboard\n\n[![Build - CI](https://github.com/xaviersolau/TableModel/actions/workflows/build-ci.yml/badge.svg)](https://github.com/xaviersolau/TableModel/actions/workflows/build-ci.yml)\n[![Coverage Status](https://coveralls.io/repos/github/xaviersolau/TableModel/badge.svg?branch=main)](https://coveralls.io/github/xaviersolau/TableModel?branch=main)\n[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)\n\n| Package                             | Nuget.org | Pre-release |\n|-------------------------------------|-----------|-------------|\n|**SoloX.TableModel**                 |[![NuGet Beta](https://img.shields.io/nuget/v/SoloX.TableModel.svg)](https://www.nuget.org/packages/SoloX.TableModel)|[![NuGet Beta](https://img.shields.io/nuget/vpre/SoloX.TableModel.svg)](https://www.nuget.org/packages/SoloX.TableModel)|\n|**SoloX.TableModel.Server**          |[![NuGet Beta](https://img.shields.io/nuget/v/SoloX.TableModel.Server.svg)](https://www.nuget.org/packages/SoloX.TableModel.Server)|[![NuGet Beta](https://img.shields.io/nuget/vpre/SoloX.TableModel.svg)](https://www.nuget.org/packages/SoloX.TableModel)|\n\n## License and credits\n\nBlazorLayout project is written by Xavier Solau. It's licensed under the MIT license.\n\n * * *\n\n## Installation\n\nYou can checkout this Github repository or you can use the NuGet packages:\n\n**Install using the command line from the Package Manager:**\n```bash\nInstall-Package SoloX.TableModel -version 1.0.0\nInstall-Package SoloX.TableModel.Server -version 1.0.0\n```\n\n**Install using the .Net CLI:**\n```bash\ndotnet add package SoloX.TableModel --version 1.0.0\ndotnet add package SoloX.TableModel.Server --version 1.0.0\n```\n\n**Install editing your project file (csproj):**\n```xml\n\u003cPackageReference Include=\"SoloX.TableModel\" Version=\"1.0.0\" /\u003e\n\u003cPackageReference Include=\"SoloX.TableModel.Server\" Version=\"1.0.0\" /\u003e\n```\n\n## Use case\n\nLet's say that you write an application that needs to get some data from a server API. Usually\nyou start thinking that you just need the load the data and to send it directly to the client.\nThen you realize that you gonna need to make a query with a filter on a given property but\nyour boss is pushing you to deliver as soon as possible so you just add a Query strings and use it\nin your Entity Framework query through some custom methods in your repository.\nFinally, you end up with multiple query string and many custom methods to implement all specific\nrequests.\n\nWell, this is not really the best solution in terms of scalability and or maintainability. Don't\nyou agree?\n\nThis project is here to help you in a very easy way to add an end-point that is going to serve\nyour data and to be able to filter and/or sort your data. In addition, it's also going to help you\nto make partial data request if you don't need to load all data at once!\n\nAnother nice point is that from the client side this project will allow you to make data filter\nas easy as writing a lambda expression!\n\n## How to use it\n\nNote that you can find code examples in this repository at this location: `src/examples`.\n\n### Create a shared Dto assembly\n\nFirst of all, the Dto objects must be shared between the server and the client.\n\nFor instance if you want to use a `WeatherForecastDto`:\n* The server serving the data will need to have a reference on it to define the data mapping between\nthe internal entities and the transmitted Dto.\n* The client requesting the data will need to have a reference on it to register the TableData and to\nuse it. Thanks to Blazor this is also possible on web client side.\n\n### Set up the server\n\n#### Add the Nuget\n\nOn the server API, you can add the `SoloX.TableModel.Server` Nuget. It provides all TableModel support\nand the controller base implementations.\n\n#### Register services\n\nIn order to set up the TableModel services to serve table data, you just need to call the\nextension method `AddTableModelServer` from `SoloX.TableModel.Server` name space.\n\nHere is a quick example to set up some data (in our case `WeatherForecastDto`) to serve from a custom\ntable data `WeatherForecastTableData`.\n\n```csharp\nservices.AddTableModelServer(\n    builder =\u003e\n    {\n        // Here we are going to serve some data from a custom ITableData implementation.\n        builder.UseQueryableTableData\u003cWeatherForecastDto, WeatherForecastTableData\u003e();\n    });\n```\n\nLet's say that our `WeatherForecastTableData` will load our data from an Entity Framework\nDdContext `WeatherForecastDbContext`. All you need to do to write your `WeatherForecastTableData`\nis to define the mapping between your entity and your DTO.\n\nFor instance if we have an entity `WeatherForecastEntity` and a DTO `WeatherForecastDto` the\nTableData will look like this:\n\n```csharp\npublic class WeatherForecastTableData : AQueryableTableData\u003cWeatherForecastDto\u003e\n{\n    private readonly WeatherForecastDbContext dbContext;\n\n    public WeatherForecastTableData(WeatherForecastDbContext dbContext)\n    {\n        this.dbContext = dbContext;\n    }\n\n    protected override IQueryable\u003cWeatherForecastDto\u003e QueryData()\n    {\n        return this.dbContext.WeatherForecasts\n            .Select(enity =\u003e new WeatherForecastDto()\n            {\n                /* Initialize all properties from the entity... */\n            });\n    }\n}\n```\n\n#### Create the TableData Controller\n\nNow that our TableData is initialized, we have to create the Controller that will actually\nserve the data through the server API end-point.\n\nOnce again it is really easy to make it because we just have to create a controller class\nbased on the provided `TableDataControllerBase` implementation. You also have to declare the\nappropriate attributes to make your controller available through ASP.Net Core API stack.\n\n```csharp\n/// \u003csummary\u003e\n/// The TableDataController based on TableDataControllerBase that is providing the end-points to\n/// query table data.\n/// \u003c/summary\u003e\n/// \u003cremarks\u003e\n/// You can also use the Authorization attributes here.\n/// \u003c/remarks\u003e\n[Route(\"api/[controller]\")]\n[ApiController]\npublic class TableDataController : TableDataControllerBase\n{\n    /// \u003csummary\u003e\n    /// Setup the TableDataController with the tableDataEndPointService provided in the\n    /// SoloX.TableModel.Server package.\n    /// \u003c/summary\u003e\n    /// \u003cparam name=\"tableStructureEndPointService\"\u003eThe end-point service that is actually doing the job.\u003c/param\u003e\n    public TableDataController(ITableDataEndPointService tableDataEndPointService)\n        : base(tableDataEndPointService)\n    {\n    }\n}\n```\n\nBasically this controller will provide 3 end-points:\n\n| Url path | HTTP Method | Summary | Result |\n|---|----|---|---|\n| /api/TableData | GET | Get the list of table data available. | The table data list. |\n| ​/api​/TableData​/\\{*table data id*\\}/data | POST | Post a data request. | The requested data items. |\n| ​/api​/TableData​/\\{*table data id*\\}/count | POST | Post a data count request. | The data count. |\n\n#### Create the TableStructure Controller\n\nComing soon...\n\n### Set up the client\n\n#### Add the Nuget\n\nOn the client, you can add the `SoloX.TableModel` Nuget. It provides all TableModel support.\n\n#### Register services\n\nIn order to set up the TableModel services to use the table data, you just need to call the\nextension method `AddTableModel` from `SoloX.TableModel` name space.\n\nHere is a quick example to set up some data (in our case `WeatherForecastDto`) to be requested from\nthe server.\n\nIn the `Program.cs` file form the Wasm Blazor client, you can register the TableModel services like this:\n\n```csharp\nbuilder.services.AddTableModel(\n    tableBuilder =\u003e\n    {\n        tableBuilder.UseRemoteTableData\u003cWeatherForecastDto\u003e(\n            config =\u003e\n            {\n                config.HttpClient = new HttpClient\n                {\n                    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress + \"api/TableData/\")\n                };\n            });\n    });\n```\n\n### Using the TableData\n\nWhether or not we are in the server or the client project, the TableData can be used the same way.\n\n#### Get TableData from ITableDataRepository\n\nOnce the table is registered in the services configuration, you just need to inject the `ITableDataRepository`.\nFrom this repository we can get a `TableData` instance associated to a given data type.\n\n\u003e\n\u003e Note that creating a TableData instance does not actually load any data. The data will be loaded at query time.\n\u003e\n\nLet's have a look at how we get a `TableData` instance with an example:\n\n```csharp\n// Inject the TableDataRepository to get the registered TableData.\n// It also can be injected as a constructor parameter.\n[Inject]\npublic ITableDataRepository TableDataRepository { get; set; }\n\n// Let's Load some data.\npublic async Task\u003cIEnumerable\u003cWeatherForecastDto\u003e\u003e LoadDataAsync()\n{\n    // Get the TableData associated to the type WeatherForecastDto from the TableDataRepository type.\n    var weatherForecastTableDate = await TableDataRepository.GetTableDataAsync\u003cWeatherForecastDto\u003e();\n\n    /// Then load data...\n}\n```\n\n#### Basic queries\n\nNow that we have got our TableData instance, we can make data queries to get the whole data or just a data page:\n\n```csharp\n// First get data count from the TableData.\nvar dataCount = await weatherForecastTableDate.GetDataCountAsync();\n\n// Get all data from the TableData.\nvar allData = await weatherForecastTableDate.GetDataAsync();\n\n// But we may only need a data page from a given data index and page size.\nvar someDataPage = await weatherForecastTableDate.GetDataPageAsync(index, size);\n```\n\n#### Sorting data\n\nIf you need to get sorted data on a given property, thanks to the Lambda expression, it's really easy:\n\n```csharp\n// Inject the ITableFactory to get a ITableSorting instance.\n// It also can be injected as a constructor parameter.\n[Inject]\npublic ITableFactory TableFactory { get; set; }\n\n// Let's Load some sorted data.\npublic async Task\u003cIEnumerable\u003cWeatherForecastDto\u003e\u003e LoadSortedDataAsync(\n    ITableData\u003cWeatherForecastDto\u003e weatherForecastTableDate)\n{\n    // Create the TableSorting.\n    var tableSorting = TableFactory.CreateTableSorting\u003cWeatherForecastDto\u003e();\n\n    // Register a sorting operation on TemperatureC property.\n    tableSorting.Register(wf =\u003e wf.TemperatureC, SortingOrder.Ascending);\n\n    // Run the query using the tableSorting.\n    var sortedData = weatherForecastTableDate.GetDataAsync(tableSorting);\n}\n```\n\n#### Filter data\n\nHere again, it's easy to use lambda to setup the data filters:\n\n```csharp\n// Inject the ITableFactory to get a ITableFilter instance.\n// It also can be injected as a constructor parameter.\n[Inject]\npublic ITableFactory TableFactory { get; set; }\n\n// Let's Load some filtered data.\npublic async Task\u003cIEnumerable\u003cWeatherForecastDto\u003e\u003e LoadFilteredDataAsync(\n    ITableData\u003cWeatherForecastDto\u003e weatherForecastTableDate)\n{\n    // Create the TableFilter.\n    var tableFilter = TableFactory.CreateTableFilter\u003cWeatherForecastDto\u003e();\n\n    // Register a filter operation on Summary property.\n    tableFilter.Register(wf =\u003e wf.Summary, s =\u003e s.Contains(\"Hot\"));\n\n    // Run the query using the tableFilter.\n    var filteredData = weatherForecastTableDate.GetDataAsync(tableFilter);\n}\n```\n\n\u003e\n\u003e Note that there are limitations using lambda expression in filter: basically you will get the same\n\u003e limitation than the one you have in the underling queriable data implementation. For example if you\n\u003e are using EF Core behind the scene, you will have the same limitations.\n\u003e \n\u003e In addition it is impossible in the current version to use EF.Functions in your filter expression.\n\u003e\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaviersolau%2Ftablemodel","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fxaviersolau%2Ftablemodel","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fxaviersolau%2Ftablemodel/lists"}