ASP.NET Core

Server-side query building with Tag Helpers, View Components, and session-based state management. No JavaScript required.

Installation#

Install the ASP.NET Core integration package via the .NET CLI.

shell
dotnet add package NetQueryBuilder.AspNetCore
Note

The NetQueryBuilder.AspNetCore package includes NetQueryBuilder and NetQueryBuilder.EntityFramework as dependencies, so a single install command is all you need.

Setup#

Setting up NetQueryBuilder in an ASP.NET Core application requires four steps: registering services, configuring middleware, adding Tag Helpers, and including the stylesheet.

1. Register services in Program.cs

Use the AddNetQueryBuilder<TContext> extension method to register all required services at once. This registers the options as a singleton, IQuerySessionService as a singleton, and IQueryConfigurator as scoped to match the DbContext lifetime.

csharp
var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRazorPages();
builder.Services.AddDbContext<YourDbContext>(options =>
    options.UseSqlServer(connectionString));

// Single line registers all NetQueryBuilder services
builder.Services.AddNetQueryBuilder<YourDbContext>(options =>
{
    options.SessionTimeout = TimeSpan.FromMinutes(30);
    options.DefaultPageSize = 10;
});

2. Configure middleware

Call UseNetQueryBuilder() after UseRouting() and before MapRazorPages(). This configures session state and static file serving for the built-in CSS assets.

csharp
var app = builder.Build();

app.UseRouting();
app.UseNetQueryBuilder();  // Handles session + static files
app.MapRazorPages();

app.Run();

3. Add Tag Helpers in _ViewImports.cshtml

Register the Tag Helpers so Razor Pages can recognize the nqb- elements.

html
@addTagHelper *, NetQueryBuilder.AspNetCore

4. Include the stylesheet

Add the default NetQueryBuilder stylesheet to your layout or page.

html
<link rel="stylesheet"
      href="~/_content/NetQueryBuilder.AspNetCore/css/netquerybuilder.css" />
No JavaScript required

The ASP.NET Core integration uses standard HTML forms and server-side rendering. All interactions happen through regular HTTP POST requests handled by Razor Pages, so there are no JavaScript dependencies to manage.

Page Model#

Create a Razor Page that inherits from NetQueryPageModelBase. The base class provides session management, property binding, and automatic handler dispatch using reflection so you do not need entity-type switch statements.

QueryBuilder.cshtml.cs

csharp
using NetQueryBuilder.AspNetCore.Pages;
using NetQueryBuilder.AspNetCore.Services;
using NetQueryBuilder.Configurations;

public class QueryBuilderModel : NetQueryPageModelBase
{
    public QueryBuilderModel(
        IQuerySessionService sessionService,
        IQueryConfigurator configurator)
        : base(sessionService, configurator) { }

    public void OnGet() { }

    // Base class automatically handles:
    // - OnPostChangeEntity()
    // - OnPostToggleProperty()
    // - OnPostUpdateProperties()
    // - OnPostAddCondition()
    // - OnPostRemoveCondition()
    // - OnPostAddGroup()
    // - OnPostExecuteQueryAsync()
    // - OnPostChangePageAsync()
    // - OnPostClearQuery()
}

QueryBuilder.cshtml

html
@page
@model QueryBuilderModel

<h1>Query Builder</h1>

<form method="post">
    <nqb-entity-selector session-id="@Model.SessionId"></nqb-entity-selector>
    <nqb-property-selector session-id="@Model.SessionId"></nqb-property-selector>
    <nqb-condition-builder session-id="@Model.SessionId"></nqb-condition-builder>

    @await Component.InvokeAsync("ExpressionDisplay", new { sessionId = Model.SessionId })

    <button type="submit"
            asp-page-handler="ExecuteQuery"
            class="nqb-button nqb-button-primary">
        Run Query
    </button>
</form>

@if (Model.State.Results != null)
{
    @await Component.InvokeAsync("QueryResults", new { sessionId = Model.SessionId })
    @await Component.InvokeAsync("Pagination", new { sessionId = Model.SessionId })
}
Automatic handler dispatch

The base class uses reflection to dispatch query execution to the correct generic method based on the selected entity type. This eliminates if-else chains and lets you support any number of entity types without modifying the page model.

Tag Helpers#

NetQueryBuilder provides three Tag Helpers that render server-side HTML form controls. All Tag Helpers use the nqb- prefix for namespace isolation and require a session-id attribute to retrieve session state.

Entity Selector

Renders a dropdown allowing the user to choose which entity type to query. Submitting the form triggers the OnPostChangeEntity handler.

html
<nqb-entity-selector session-id="@Model.SessionId"></nqb-entity-selector>
Attribute Type Description
session-id string The session identifier used to retrieve available entity types from the configurator.

Property Selector

Renders checkboxes for selecting which entity properties to include in query results. Each checkbox triggers the OnPostToggleProperty handler, or use batch mode with OnPostUpdateProperties.

html
<nqb-property-selector session-id="@Model.SessionId"></nqb-property-selector>
Attribute Type Description
session-id string The session identifier used to retrieve available properties for the selected entity.

Condition Builder

Renders the full condition building UI with property dropdowns, operator selectors, and value inputs. Supports adding, removing, and grouping conditions through standard form posts.

html
<nqb-condition-builder session-id="@Model.SessionId"></nqb-condition-builder>
Attribute Type Description
session-id string The session identifier used to retrieve the current query and its conditions.

View Components#

View Components handle the display of query results and related UI. They are invoked with Component.InvokeAsync in Razor views.

QueryResults

Displays query results in a responsive HTML table. Columns are determined by the selected properties. Renders nothing when no results are available.

html
@await Component.InvokeAsync("QueryResults", new { sessionId = Model.SessionId })

Pagination

Renders page navigation controls. Links submit to the OnPostChangePageAsync handler on the base page model. Hidden when the result set fits on a single page.

html
@await Component.InvokeAsync("Pagination", new { sessionId = Model.SessionId })

ExpressionDisplay

Shows the generated LINQ expression tree as a human-readable string. Useful during development and debugging to verify the query being built.

html
@await Component.InvokeAsync("ExpressionDisplay", new { sessionId = Model.SessionId })

Session Management#

The IQuerySessionService manages query state between HTTP requests using a thread-safe ConcurrentDictionary. Each user gets a unique session ID stored in an HTTP session cookie.

csharp
// Get the current session state
var state = sessionService.GetState(sessionId);

// Access the current query
var query = state.Query;

// Access stored results
var results = state.Results;

// Create or refresh a query for an entity type
var query = sessionService.GetOrCreateQuery(sessionId, entityType, configurator);

Session state tracks LastAccessTime using DateTime.UtcNow on every access. A background SessionCleanupService periodically removes expired sessions based on the configured timeout interval.

Fresh DbContext on every request

The session service always creates a new IQuery instance with a fresh DbContext on every request, transferring conditions from the previous query. This avoids ObjectDisposedException errors that would occur if the disposed context from a prior request were reused.

Custom Handlers#

You can add custom POST handlers alongside the base functionality. The base class exposes SessionService and Configurator as protected fields for use in derived classes.

csharp
public class QueryBuilderModel : NetQueryPageModelBase
{
    public QueryBuilderModel(
        IQuerySessionService sessionService,
        IQueryConfigurator configurator)
        : base(sessionService, configurator) { }

    public void OnGet() { }

    // Custom handler for exporting results to CSV
    public async Task<IActionResult> OnPostExportAsync()
    {
        var state = SessionService.GetState(SessionId);

        if (state.Results == null)
            return RedirectToPage();

        var csv = GenerateCsv(state.Results);
        return File(
            Encoding.UTF8.GetBytes(csv),
            "text/csv",
            "export.csv");
    }
}

Add the corresponding button in the Razor view.

html
<button type="submit"
        asp-page-handler="Export"
        class="nqb-button">
    Export to CSV
</button>

Configuration Options#

Configure NetQueryBuilder behavior through the NetQueryBuilderOptions class passed to AddNetQueryBuilder<TContext>.

Property Type Default Description
SessionTimeout TimeSpan 30 minutes Duration before an idle session expires and is eligible for cleanup.
DefaultPageSize int 10 Number of results displayed per page by default.
MaxPageSize int 100 Maximum allowed page size to prevent excessively large result sets.
UseSessionStorage bool true Whether to enable session-based state storage for query persistence across requests.
SessionCleanupInterval TimeSpan 5 minutes Interval between background cleanup runs. Set to TimeSpan.Zero to disable.
EnableSessionCleanup bool true Whether to run the background SessionCleanupService that removes expired sessions.
csharp
builder.Services.AddNetQueryBuilder<AppDbContext>(options =>
{
    options.SessionTimeout = TimeSpan.FromHours(1);
    options.DefaultPageSize = 25;
    options.MaxPageSize = 200;
    options.SessionCleanupInterval = TimeSpan.FromMinutes(10);
});

Styling#

The included stylesheet provides a clean, accessible design. All CSS classes are prefixed with nqb- for namespace isolation. Customize the appearance by overriding CSS custom properties.

CSS variables

css
/* Override in your own stylesheet */
:root {
    --nqb-primary-color: #1E88E5;
    --nqb-border-radius: 4px;
    --nqb-spacing: 1rem;
}

Custom stylesheet override

Add your own stylesheet after the default one to override specific styles without modifying the library CSS.

html
<!-- Default NetQueryBuilder styles -->
<link rel="stylesheet"
      href="~/_content/NetQueryBuilder.AspNetCore/css/netquerybuilder.css" />

<!-- Your custom overrides -->
<link rel="stylesheet"
      href="~/css/custom-query-builder.css" />

Accessibility#

All rendered HTML follows WAI-ARIA best practices. The Tag Helpers generate semantic markup with proper accessibility attributes built in.