{"id":24578224,"url":"https://github.com/byteaether/querylink","last_synced_at":"2025-04-23T20:16:42.960Z","repository":{"id":251364734,"uuid":"836631262","full_name":"ByteAether/QueryLink","owner":"ByteAether","description":"QueryLink simplifies the integration of UI components with backend data sources, making it easier to manage filters and sorting operations with minimal code.","archived":false,"fork":false,"pushed_at":"2025-03-16T15:10:37.000Z","size":152,"stargazers_count":4,"open_issues_count":0,"forks_count":2,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-04-23T20:16:21.488Z","etag":null,"topics":["blazor","csharp","dotnet","iqueryable","querystring"],"latest_commit_sha":null,"homepage":"https://byteaether.github.io/series/byteaether-querylink/","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/ByteAether.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-08-01T08:40:34.000Z","updated_at":"2025-04-21T16:02:12.000Z","dependencies_parsed_at":null,"dependency_job_id":"b4d18a7a-7ea4-4446-aec3-797793d60a03","html_url":"https://github.com/ByteAether/QueryLink","commit_stats":null,"previous_names":["byteaether/querylink","querylink/querylink"],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FQueryLink","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FQueryLink/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FQueryLink/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ByteAether%2FQueryLink/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ByteAether","download_url":"https://codeload.github.com/ByteAether/QueryLink/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250506146,"owners_count":21441723,"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","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","csharp","dotnet","iqueryable","querystring"],"created_at":"2025-01-23T23:58:11.627Z","updated_at":"2025-04-23T20:16:42.946Z","avatar_url":"https://github.com/ByteAether.png","language":"C#","readme":"# ![QueryLink from ByteAether](assets/header.png)\n\n[![License](https://img.shields.io/github/license/ByteAether/QueryLink?logo=github\u0026label=License)](https://github.com/ByteAether/QueryLink/blob/main/LICENSE)\n[![NuGet Version](https://img.shields.io/nuget/v/ByteAether.QueryLink?logo=nuget\u0026label=Version)](https://www.nuget.org/packages/ByteAether.QueryLink/)\n[![NuGet Downloads](https://img.shields.io/nuget/dt/ByteAether.QueryLink?logo=nuget\u0026label=Downloads)](https://www.nuget.org/packages/ByteAether.QueryLink/)\n[![GitHub Build Status](https://img.shields.io/github/actions/workflow/status/ByteAether/QueryLink/build-and-test.yml?logo=github\u0026label=Build%20%26%20Test)](https://github.com/ByteAether/QueryLink/actions/workflows/build-and-test.yml)\n\n\u003cimg align=\"right\" width=\"100px\" src=\"assets/logo.png\" /\u003e\n\nQueryLink is a NuGet package designed to simplify the integration of UI components such as datagrids and datatables with backend `IQueryable`-based data sources. This library provides a seamless way to link these two parts of a system with minimal code, making it easier to manage filters and sorting operations.\n\n## Features\n\n![.NET 8.0](https://img.shields.io/badge/.NET-8.0-brightgreen)\n![.NET 6.0](https://img.shields.io/badge/.NET-6.0-brightgreen)\n![.NET Standard 2.1](https://img.shields.io/badge/.NET-Standard_2.1-yellow)\n\n- **Filter Definitions:** Define filters with various operators to refine your data queries.\n- **Order Definitions:** Specify sorting criteria to order your data.\n- **Overrides:** Customize filter and order operations with expression-based overrides.\n- **Query String Conversion:** Easily convert filter and order definitions to and from query strings.\n- **IQueryable Extensions:** Apply filter and order definitions directly to `IQueryable` sources.\n\n## Installation\n\nInstall the latest stable package via NuGet:\n\n```sh\ndotnet add package ByteAether.QueryLink\n```\n\nUse the `--version` option to specify a [preview version](https://www.nuget.org/packages/ByteAether.QueryLink/absoluteLatest) to install.\n\n## Usage\n\n### Definitions\n\nThe `Definitions` class allows you to specify filters and orders for your data queries.\n\nThis example demonstrates how to create filter and order definitions using the `Definitions` class.\n\n```csharp\nvar definitions = new Definitions\n{\n    Filters = [\n        new(\"Name\", FilterOperator.Eq, \"John\"),\n        new(\"Age\", FilterOperator.Gt, 30)\n    ],\n    Orders = [\n        new(\"Name\", false),\n        new(\"Age\", true)\n    ]\n};\n```\n\n### Overrides\n\nThe `Overrides` class allows you to customize filter and order operations using expression-based overrides.\n\nThis example shows how to create overrides for filter and order operations using the `Overrides` class.\n\n```csharp\nvar overrides = new Overrides\u003cPerson\u003e\n{\n    Filter = [\n        new(p =\u003e p.Name, p =\u003e p.FullName)\n    ],\n    Order = [\n        new(p =\u003e p.Name, p =\u003e p.FullName)\n    ]\n};\n```\n\n### Query String Conversion\n\nConvert filter and order definitions to and from query strings using the `HttpExtensions` class.\n\nThis example demonstrates how to convert filter and order definitions to and from query strings using the `HttpExtensions` class.\n\n```csharp\nstring queryString = definitions.ToQueryString();\nDefinitions parsedDefinitions = Definitions.FromQueryString(queryString);\n```\n\n### Applying Definitions to IQueryable\n\nApply filter and order definitions directly to `IQueryable` sources using the `QueryableExtensions` class.\n\nThis example shows how to apply filter and order definitions to an `IQueryable` source using the `QueryableExtensions` class.\n\n```csharp\nIQueryable\u003cPerson\u003e query = dbContext.People.AsQueryable();\nquery = query.Apply(definitions, overrides);\n```\n\n## Examples\n\n### Filtering and Sorting\n\nThis example demonstrates filtering and sorting using the `Definitions` class and applying them to an `IQueryable` source.\n\n```csharp\nvar definitions = new Definitions\n{\n    Filters = [\n        new(\"Name\", FilterOperator.Eq, \"John\"),\n        new(\"Age\", FilterOperator.Gt, 30)\n    ],\n    Orders = [\n        new(\"Name\", false),\n        new(\"Age\", true)\n    ]\n};\n\nIQueryable\u003cPerson\u003e query = dbContext.People.AsQueryable();\nquery = query.Apply(definitions);\n```\n\n### Using Overrides\n\nThis example shows how to use overrides to customize filter and order operations and apply them to an `IQueryable` source.\n\n```csharp\nvar overrides = new Overrides\u003cPerson\u003e\n{\n    Filter = [\n        new(p =\u003e p.Name, p =\u003e p.FullName)\n    ],\n    Order = [\n        new(p =\u003e p.Name, p =\u003e p.FullName)\n    ]\n};\n\nIQueryable\u003cPerson\u003e query = dbContext.People.AsQueryable();\nquery = query.Apply(definitions, overrides);\n```\n\n### Comprehensive Example: Using MudBlazor DataGrid and EF Core\n\nThis example demonstrates how to integrate QueryLink with MudBlazor DataGrid and EF Core. The `LoadServerData` method reads the state of the MudBlazor DataGrid, creates a QueryLink definition set out of it, and sends the definitions over an HTTP API using `ToQueryString` and `FromQueryString`. The `PersonService` class contains the overrides and applies the definitions to the `IQueryable` source. The `PeopleController` handles the API requests, reads the full query string from the request, and returns the filtered and sorted data. The produced query string is directly included in the URL, and the definitions are parsed from the full query string.\n\n```csharp\n// Define your EF Core DbContext and entity\npublic class ApplicationDbContext : DbContext\n{\n    public DbSet\u003cPerson\u003e People { get; set; }\n}\n\npublic class Person\n{\n    public int Id { get; set; }\n    public string Name { get; set; }\n    public int Age { get; set; }\n    public string FullName =\u003e $\"{Name} Doe\";\n}\n\n// In your service or controller\npublic class PersonService\n{\n    private readonly ApplicationDbContext _context;\n\n    public PersonService(ApplicationDbContext context)\n    {\n        _context = context;\n    }\n\n    public IQueryable\u003cPerson\u003e GetPeople(Definitions definitions)\n    {\n        var overrides = new Overrides\u003cPerson\u003e\n        {\n            Filter = [\n                new(p =\u003e p.Name, p =\u003e p.FullName)\n            ],\n            Order = [\n                new(p =\u003e p.Name, p =\u003e p.FullName)\n            ]\n        };\n\n        var query = _context.People.AsQueryable();\n        return query.Apply(definitions, overrides);\n    }\n}\n\n// In your API controller\n[ApiController]\n[Route(\"api/[controller]\")]\npublic class PeopleController : ControllerBase\n{\n    private readonly PersonService _personService;\n\n    public PeopleController(PersonService personService)\n    {\n        _personService = personService;\n    }\n\n    [HttpGet]\n    public IActionResult GetPeople()\n    {\n        var queryString = Request.QueryString.ToString();\n        var definitions = Definitions.FromQueryString(queryString);\n        var people = _personService.GetPeople(definitions);\n        return Ok(people);\n    }\n}\n\n// In your Blazor component\n@page \"/people\"\n@inject HttpClient Http\n\n\u003cMudDataGrid\n    T=\"Person\"\n    Items=\"people\"\n    Hover=\"true\"\n    Sortable=\"true\"\n    Filterable=\"true\"\n    Striped=\"true\"\n    Pagination=\"true\"\n    ServerData=\"LoadServerData\"\n\u003e\n    \u003cToolBarContent\u003e\n        \u003cMudText typo=\"Typo.h6\"\u003ePeople\u003c/MudText\u003e\n    \u003c/ToolBarContent\u003e\n    \u003cColumns\u003e\n        \u003cColumn T=\"Person\" Field=\"@nameof(Person.Name)\" Title=\"Name\" Sortable=\"true\" Filterable=\"true\" /\u003e\n        \u003cColumn T=\"Person\" Field=\"@nameof(Person.Age)\" Title=\"Age\" Sortable=\"true\" Filterable=\"true\" /\u003e\n    \u003c/Columns\u003e\n\u003c/MudDataGrid\u003e\n\n@code {\n    private IEnumerable\u003cPerson\u003e people = new List\u003cPerson\u003e();\n\n    private async Task\u003cGridData\u003cPerson\u003e\u003e LoadServerData(GridState\u003cPerson\u003e state)\n    {\n        var definitions = new Definitions\n        {\n            Filters = state.Filters.Select(f =\u003e new FilterDefinition\u003cobject?\u003e(f.Field, GetFilterOperator(f.Operator), f.Value)).ToList(),\n            Orders = state.Sorts.Select(s =\u003e new OrderDefinition(s.Field, s.Direction == SortDirection.Descending)).ToList()\n        };\n\n        var queryString = definitions.ToQueryString();\n        var response = await Http.GetFromJsonAsync\u003cList\u003cPerson\u003e\u003e($\"api/people?{queryString}\");\n\n        var totalItems = response.Count();\n        var items = response.Skip(state.Page * state.PageSize).Take(state.PageSize).ToList();\n\n        return new GridData\u003cPerson\u003e { Items = items, TotalItems = totalItems };\n    }\n\n    private FilterOperator GetFilterOperator(FilterOperator mudOperator)\n    {\n        return mudOperator switch\n        {\n            FilterOperator.Contains =\u003e FilterOperator.Has,\n            FilterOperator.Equals =\u003e FilterOperator.Eq,\n            FilterOperator.GreaterThan =\u003e FilterOperator.Gt,\n            FilterOperator.GreaterThanOrEqual =\u003e FilterOperator.Gte,\n            FilterOperator.LessThan =\u003e FilterOperator.Lt,\n            FilterOperator.LessThanOrEqual =\u003e FilterOperator.Lte,\n            FilterOperator.NotEqual =\u003e FilterOperator.Neq,\n            FilterOperator.StartsWith =\u003e FilterOperator.Sw,\n            FilterOperator.EndsWith =\u003e FilterOperator.Ew,\n            _ =\u003e throw new ArgumentOutOfRangeException(nameof(mudOperator), mudOperator, null)\n        };\n    }\n}\n```\n\n## Filter Operators\n\nThe library provides a variety of filter operators to refine your data queries. Here is a list of all the available filter operators:\n\n- **Eq `=`:** Equals\n- **Neq `!=`:** Not equals\n- **Gt `\u003e`:** Greater than\n- **Gte `\u003e=`:** Greater than or equal to\n- **Lt `\u003c`:** Less than\n- **Lte `\u003c=`:** Less than or equal to\n- **Has `=*`:** Contains\n- **Nhas `!*`:** Does not contain\n- **In `[]`:** In a list\n- **Nin `![]`:** Not in a list\n- **Sw `^`:** Starts with\n- **Nsw `!^`:** Does not start with\n- **Ew `$`:** Ends with\n- **New `!$`:** Does not end with\n\n## FAQ\n\n### Why is there no pagination support?\n\nPagination depends heavily on the underlying data persistence technology and requires specific implementations for each technology. It is easy to write your own pagination logic and apply it to `IQueryable` on top of what our library provides.\n\n### How can I create my own custom conditions?\n\nThe full functionality of LINQ is still available. You are free to write any `.Where()` conditions and apply them to `IQueryable`. Our library does not block you from doing that.\n\n### I need projections; the raw data models are not enough for me.\n\nYou can use any library that can map objects from one to another or use your own mapper code. Our library does not limit you in any way and will work with the dataset you provide in the form of `IQueryable\u003cT\u003e`, whatever the `T` may be.\n\n## Contributing\n\nWe welcome all contributions! You can:\n\n * **Open a Pull Request:** Fork the repository, create a branch, make your changes, and submit a pull request to the `main` branch.\n * **Report Issues:** Found a bug or have a suggestion? [Open an issue](https://github.com/ByteAether/Ulid/issues) with details.\n\nThank you for helping improve the project!\n\n## License\n\nThis project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.\n\n---\n\nQueryLink simplifies the integration of UI components with backend data sources, making it easier to manage filters and sorting operations with minimal code.\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteaether%2Fquerylink","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbyteaether%2Fquerylink","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbyteaether%2Fquerylink/lists"}