# Sockethead — Full Documentation > A collection of .NET utilities for ASP.NET Core including grid controls, audit logging, and common extensions. ## Overview Sockethead is a set of NuGet packages for ASP.NET Core developers: - **Sockethead.Razor** — SimpleGrid and TwoColumnGrid controls, alerts, client time rendering, PRG support, diagnostics - **Sockethead.EFCore** — Entity Framework Core audit logging with configurable cleanup policies - **Sockethead.Common** — Queryable, string, collection, JSON, and date extension methods All library projects multi-target `net6.0`, `net8.0`, and `net10.0`. Sockethead.Common also supports `netstandard2.1`. --- # Sockethead.Razor ## Installation ``` PM> Install-Package Sockethead.Razor ``` Optionally add to `ViewImports.cshtml`: ```csharp @using Sockethead.Razor.Grid; ``` No JavaScript or CSS includes are necessary for the classic theme. The Modern theme includes its own self-contained CSS. ## SimpleGrid SimpleGrid is an ASP.NET Core Grid Control that renders `IQueryable` data as HTML tables with sorting, paging, search, theming, and extensive customization. ### Basic Usage Model: ```csharp public class Movie { [Display(Order = 2)] public string Name { get; set; } [Display(Name = "Movie Director", Order = 1)] public string Director { get; set; } [DisplayName("Movie Genre")] public string Genre { get; set; } [Display(AutoGenerateField = true)] public int? Released { get; set; } [Display(AutoGenerateField = false)] public ICollection Cast { get; set; } } ``` View: ```csharp @model IQueryable @(await Html .SimpleGrid(Model) .AddColumnsForModel() .RenderAsync()) ``` Controller: ```csharp [HttpGet] public IActionResult BasicUsage() { return View(SampleData.Movies.AsQueryable()); } ``` ### Column Selection Automatically select columns from model properties (respects `[Display(AutoGenerateField = false)]`): ```csharp .AddColumnsForModel() ``` Select specific columns: ```csharp .AddColumnFor(movie => movie.Name) .AddColumnFor(movie => movie.Director) .AddColumn(col => col.For(movie => movie.Genre)) ``` `AddColumnFor` is shortcut syntax for `AddColumn` followed by `For`. Remove columns: ```csharp .RemoveColumn(1) // by zero-based index .RemoveColumn("Released") // by column header ``` ### Column Ordering Order columns using `[Display(Order = N)]` attribute: ```csharp .AddColumnsForModel() .OrderColumns() ``` Only works on columns added via `AddColumnsForModel()`. ### Column Headers Headers are resolved in this order: 1) `[Display(Name = "...")]`, 2) `[DisplayName("...")]`, 3) Property name. ```csharp .AddColumn(col => col.For(movie => movie.Name)) // from expression .AddColumn(col => col.HeaderFor(movie => movie.Director).DisplayAs(...)) // explicit expression .AddColumn(col => col.Header("GENRE").DisplayAs(movie => movie.Genre)) // explicit string ``` Hide all headers: ```csharp .NoHeaders() ``` ### Column Display ```csharp .AddColumn(col => col.For(movie => movie.Name)) // standard .AddColumn(col => col.For(movie => movie.Genre).DisplayAs(movie => movie.Genre.ToUpper())) // transform .AddColumn(col => col.Header("Director").DisplayAs(movie => $"Directed by {movie.Director}")) .AddColumn(col => col.Display("static value")) // static ``` Display as bullet list: ```csharp .AddColumn(col => col.Header("Cast").DisplayAsList(movie => movie.Cast)) ``` ### HTML Encoding Encoding is enabled by default. Disable to render raw HTML: ```csharp .AddColumn(col => col.For(movie => movie.Name).Encoded(false)) ``` ### Links ```csharp .AddColumn(col => col .For(movie => movie.Name) .LinkTo(model => $"https://example.com/search?q={WebUtility.UrlEncode(model.Name)}")) .AddColumn(col => col .Display("Details") .LinkTo( linkBuilder: model => $"/details/{model.Id}", target: "_blank", css: css => css.AddClass("btn btn-sm btn-primary"))) ``` ### Sorting Enable sorting for the entire grid, then configure per-column: ```csharp @(await Html .SimpleGrid(Model) .AddColumnFor(movie => movie.Name) // sortable by default with For() .AddColumn(col => col.For(movie => movie.Released).Sortable(false)) // disable sort .AddColumn(col => col.Header("Sort by Released") .DisplayAs(movie => movie.Director) .SortableBy(movie => movie.Released)) // sort by different field .DefaultSortBy(movie => movie.Name, SortOrder.Descending) // initial sort .Sortable() // enable grid sorting .RenderAsync()) ``` Adding `Sortable(true)` or `SortableBy(...)` to a column has no effect unless the entire grid is `.Sortable()`. ### Pagination ```csharp .AddPager(options => { options.RowsPerPage = 5; options.DisplayPagerTop = true; options.DisplayPagerBottom = true; options.DisplayTotal = true; options.RowsPerPageOptions = new[] { 5, 10, 20, 50, 100 }; }) ``` Pager position shortcuts: `options.Top()`, `options.Botttom()`, `options.TopAndBottom()`. When `HideIfTooFewRows` is true (default) and all rows fit on one page, navigation buttons are hidden but the total count and rows-per-page selector remain visible. ### Search Each `AddSearch` call adds a filter to the search dropdown: ```csharp .AddSearch("Movie Name", (source, query) => source.Where(m => m.Name.Contains(query, StringComparison.OrdinalIgnoreCase))) .AddSearch("Genre", (source, query) => source.Where(m => m.Genre.Contains(query, StringComparison.OrdinalIgnoreCase))) ``` ### Multiple Grids Pass a unique grid ID to support independent grids on the same page: ```csharp @(await Html.SimpleGrid(Model, "Grid1") .AddColumnFor(movie => movie.Name) .AddPager(options => options.RowsPerPage = 3) .RenderAsync()) ``` ### CSS Apply CSS at column level and grid level: ```csharp .AddColumn(col => col.For(movie => movie.Director) .Css(elements => elements.Item.AddStyle("background-color:yellow"))) .Css(elements => { elements.Table.AddClass("table-striped table-hover table-bordered"); elements.Row.AddStyle("color: green"); elements.Header.AddStyle("color: blue"); }) ``` ### Row Modifiers Apply CSS to rows based on conditions: ```csharp .AddRowModifier( movie => movie.Director.Contains("James"), css => css.AddStyle("background-color:red")) ``` ### Options ```csharp .Options(options => { options.MaxRows = 100; options.NoMatchingRecordsHtml = "No records found."; // Override partial views: // options.TableViewName = "..."; // options.SearchViewName = "..."; // options.GridViewName = "..."; }) ``` ### Modern Theme Framework-agnostic theme with self-contained CSS. No Bootstrap required. ```csharp @(await Html .SimpleGrid(Model) .Theme(GridTheme.Modern) .Sortable() .DefaultSortBy(m => m.Name) .AddColumn(col => col.For(m => m.Name).Sortable()) .AddColumn(col => col.For(m => m.Director).Sortable()) .AddColumn(col => col .Header("Genre") .DisplayAs(m => $"{WebUtility.HtmlEncode(m.Genre)}") .Encoded(false) .SortableBy(m => m.Genre)) .AddColumnFor(m => m.Released) .AddSearch("Name", (source, query) => source.Where(m => m.Name.Contains(query, StringComparison.OrdinalIgnoreCase))) .AddPager(options => { options.RowsPerPage = 5; options.DisplayPagerBottom = true; options.DisplayTotal = true; options.RowsPerPageOptions = new[] { 5, 10, 20 }; }) .RenderAsync()) ``` Modern theme CSS classes: | Class | Usage | |-------|-------| | `sh-badge sh-badge-success` | Green badge | | `sh-badge sh-badge-warning` | Yellow badge | | `sh-badge sh-badge-danger` | Red badge | | `sh-badge sh-badge-info` | Blue badge | | `sh-row-warning` | Yellow row highlight (use with AddRowModifier) | | `sh-row-danger` | Red row highlight | | `sh-row-success` | Green row highlight | | `sh-actions` | Flex container for action links in a cell | ### AJAX Support Main view: ```csharp @Html.SimpleGridAjax( partialViewEndpoint: "/Samples/SimpleGrid/PartialGrid", id: "Grid1", displaySearchField: true) ``` Partial view: ```csharp @(await Html .SimpleGrid(Model) .AddColumnsForModel() .Sortable() .AddPager(options => { options.RowsPerPage = 10; options.RowsPerPageOptions = new[] { 3, 5, 10, 50 }; }) .AddSearch("Name", (source, query) => source.Where(movie => movie.Name.Contains(query))) .Options(options => { options.SearchViewName = null; }) .RenderAsync()) ``` Controller: ```csharp [HttpGet] public PartialViewResult PartialGrid() => PartialView("_PartialGrid", MovieQuery); ``` ### Forms with SimpleGrid Checkbox selection: ```csharp
@(await Html.SimpleGrid(Model.Take(5)) .AddColumn(col => col .HeaderCheckboxAll("Names") .DisplayAs(model => $"") .Encoded(false)) .AddColumnsForModel() .RenderAsync())
``` ### Embedding Grids Embed TwoColumnGrid inside SimpleGrid: ```csharp .AddColumn(col => col.Header("Movie").TwoColumnGrid(model => model)) ``` Embed SimpleGrid inside SimpleGrid: ```csharp .AddColumn(col => col.For(movie => movie.Cast) .SimpleGrid( movie => movie.Cast?.AsQueryable(), grid => grid.AddColumnsForModel())) ``` ### DateTime Handling `For()`, `DisplayExpression()`, and `ClientTime()` output `System.DateTime` as HTML `