RestLib.InMemory 2.3.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package RestLib.InMemory --version 2.3.0
                    
NuGet\Install-Package RestLib.InMemory -Version 2.3.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="RestLib.InMemory" Version="2.3.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="RestLib.InMemory" Version="2.3.0" />
                    
Directory.Packages.props
<PackageReference Include="RestLib.InMemory" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add RestLib.InMemory --version 2.3.0
                    
#r "nuget: RestLib.InMemory, 2.3.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package RestLib.InMemory@2.3.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=RestLib.InMemory&version=2.3.0
                    
Install as a Cake Addin
#tool nuget:?package=RestLib.InMemory&version=2.3.0
                    
Install as a Cake Tool

RestLib

3 lines to a production-ready REST API

Build Coverage NuGet License

RestLib is a .NET 10 library for ASP.NET Core Minimal APIs that generates CRUD endpoints from your model and repository. It bakes in secure defaults, cursor-based pagination APIs, filtering, sorting, field selection, HATEOAS hypermedia links, OpenAPI metadata, and RFC 9457 Problem Details so you can ship consistent APIs faster. Some capabilities depend on the repository adapter and may have implementation-specific limits.

Install

Install the core package:

dotnet add package RestLib

For demos, tests, and quick prototypes, add the optional in-memory adapter:

dotnet add package RestLib.InMemory

For production use with Entity Framework Core:

dotnet add package RestLib.EntityFrameworkCore

Quick Start

Create a new app:

dotnet new web -n MyApi
cd MyApi

Define a model:

public class Product
{
    public Guid Id { get; set; }
    public required string Name { get; set; }
    public decimal Price { get; set; }
}

Configure RestLib:

using RestLib;
using RestLib.InMemory;
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRestLib();
builder.Services.AddRestLibInMemory<Product, Guid>(p => p.Id, Guid.NewGuid);
builder.Services.AddOpenApi();

var app = builder.Build();

app.MapOpenApi();
app.MapScalarApiReference();

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowAnonymous();
});

app.Run();

Run the app and open the API reference at /scalar:

dotnet run

That gives you:

  • GET /api/products - list all with cursor pagination
  • GET /api/products/{id} - fetch a single resource
  • POST /api/products - create
  • PUT /api/products/{id} - replace
  • PATCH /api/products/{id} - partially update
  • DELETE /api/products/{id} - delete

EF Core Quick Start

For a database-backed path, define the same model plus a DbContext:

using Microsoft.EntityFrameworkCore;

public class Product
{
    public Guid Id { get; set; }
    public required string Name { get; set; }
    public decimal Price { get; set; }
}

public class AppDbContext : DbContext
{
    public AppDbContext(DbContextOptions<AppDbContext> options)
        : base(options)
    {
    }

    public DbSet<Product> Products => Set<Product>();
}

Register EF Core, the RestLib adapter, and ensure the database exists at startup:

using Microsoft.EntityFrameworkCore;
using RestLib;
using RestLib.EntityFrameworkCore;
using Scalar.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddRestLib();
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=app.db"));
builder.Services.AddRestLibEfCore<AppDbContext, Product, Guid>();
builder.Services.AddOpenApi();

var app = builder.Build();

using (var scope = app.Services.CreateScope())
{
    var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
    db.Database.EnsureCreated();
}

app.MapOpenApi();
app.MapScalarApiReference();

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowAnonymous();
});

app.Run();

That exposes the same CRUD endpoints as the in-memory quick start, but backed by EF Core and SQLite. If your key property follows EF Core conventions, AddRestLibEfCore can infer it automatically; otherwise provide a KeySelector in the options callback.

Why RestLib

Every backend project starts the same way: define a model, write CRUD endpoints, add validation, handle errors, set up pagination, wire Swagger, and repeat for every entity.

RestLib removes that repetition while keeping the parts that matter explicit:

  • Proper REST semantics inspired by the Zalando REST API Guidelines
  • Secure-by-default endpoints with per-operation opt-out
  • Machine-readable RFC 9457 Problem Details responses
  • Opt-in HATEOAS hypermedia links (Richardson Maturity Model Level 3)
  • Hook-based extensibility instead of controller inheritance
  • OpenAPI metadata and package-ready defaults out of the box

Features

Secure by Default

All endpoints require authorization unless you opt out per operation:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowAnonymous(RestLibOperation.GetAll, RestLibOperation.GetById);
    config.RequirePolicy(RestLibOperation.Delete, "AdminOnly");
});

Standards-Compliant Responses

RestLib follows the Zalando REST API Guidelines and uses RFC 9457 Problem Details for error payloads.

  • snake_case JSON properties
  • Cursor-based pagination API (forward-only by design; the EF Core adapter uses keyset cursors for supported stable sorts and falls back to encoded offsets otherwise — see ADR-001)
  • Structured validation and error responses
  • Consistent HTTP status codes
{
  "type": "/problems/not-found",
  "title": "Resource Not Found",
  "status": 404,
  "detail": "Product with ID '999' does not exist.",
  "instance": "/api/products/999"
}

Advanced Filtering

Enable query-string filtering with no custom parser code:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowFiltering(p => p.CategoryId, p => p.IsActive);
    config.AllowFiltering(p => p.Price, FilterOperators.Comparison);
    config.AllowFiltering(p => p.Name, FilterOperators.String);
});

Equality filters use direct query parameters:

GET /api/products?category_id=5&is_active=true

Operator filters use bracket syntax for ranges, partial matches, and set membership:

GET /api/products?price[gte]=20&price[lte]=100
GET /api/products?name[contains]=widget
GET /api/products?status[in]=active,pending

Ten operators are available: eq, neq, gt, lt, gte, lte, contains, starts_with, ends_with, and in. Each property declares which operators it supports via preset arrays (FilterOperators.Comparison, FilterOperators.String, FilterOperators.All) or individual FilterOperator values. Eq is always implicitly allowed. See ADR-013 for details.

Sorting

Control result ordering with an allow-list of sortable properties:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowSorting(p => p.Price, p => p.Name);
    config.DefaultSort("name:asc");
});
GET /api/products?sort=price:asc,name:desc&limit=20

Sort fields use snake_case names and support asc/desc directions. Disallowed fields return a 400 Problem Details response.

Rate Limiting

Integrate with ASP.NET Core's rate limiting middleware to throttle requests per operation:

builder.Services.AddRateLimiter(options =>
{
    options.AddFixedWindowLimiter("read-policy", o => { o.PermitLimit = 100; o.Window = TimeSpan.FromMinutes(1); });
    options.AddFixedWindowLimiter("write-policy", o => { o.PermitLimit = 20; o.Window = TimeSpan.FromMinutes(1); });
});

app.UseRateLimiter();

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.UseRateLimiting("read-policy", RestLibOperation.GetAll, RestLibOperation.GetById);
    config.UseRateLimiting("write-policy", RestLibOperation.Create, RestLibOperation.Update);
});

Set a default policy and exempt specific operations:

config.UseRateLimiting("default-policy");
config.DisableRateLimiting(RestLibOperation.GetById);

Rate limiting is opt-in. RestLib applies the named policy to endpoints; the application defines and registers the actual policies.

Field Selection

Return only the fields your client needs with sparse fieldsets:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.AllowFieldSelection(p => p.Id, p => p.Name, p => p.Price, p => p.CategoryId);
});
GET /api/products?fields=id,name,price

Only the selected fields are included in the response. Unknown or disallowed fields return a 400 Problem Details response. If no fields parameter is sent, the full entity is returned.

Field selection works with both GetAll (collection) and GetById (single entity) endpoints, and combines with filtering, sorting, and pagination.

Batch Operations

Create, update, patch, or delete multiple resources in a single request:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.EnableBatch(BatchAction.Create, BatchAction.Delete, BatchAction.Patch);
});
POST /api/products/batch
Content-Type: application/json

{
  "action": "create",
  "items": [
    { "name": "Keyboard", "price": 49.99 },
    { "name": "Mouse", "price": 29.99 }
  ]
}

The response reports per-item status. All succeeded returns 200; mixed results return 207 Multi-Status with individual status codes per item.

Batch size is limited to 100 items by default (configurable via RestLibOptions.MaxBatchSize). Hooks fire once per item, and validation runs per item with errors reported individually.

Enable HAL-style _links on every entity response for discoverability:

builder.Services.AddRestLib(opts =>
{
    opts.EnableHateoas = true;
});

Responses include contextual navigation links:

{
  "id": "a1b2c3d4-...",
  "name": "Keyboard",
  "price": 49.99,
  "_links": {
    "self":       { "href": "https://api.example.com/api/products/a1b2c3d4-..." },
    "collection": { "href": "https://api.example.com/api/products" },
    "update":     { "href": "https://api.example.com/api/products/a1b2c3d4-..." },
    "patch":      { "href": "https://api.example.com/api/products/a1b2c3d4-..." }
  }
}

Links are CRUD-aware: update, patch, and delete only appear when those operations are enabled on the endpoint. Batch responses include per-item links.

For custom link relations (e.g., related resources), implement IHateoasLinkProvider<TEntity, TKey>:

public class ProductLinkProvider : IHateoasLinkProvider<Product, Guid>
{
    public IEnumerable<HateoasLink> GetLinks(Product entity, Guid key, string baseUrl, string collectionPath)
    {
        yield return new HateoasLink("category", $"{baseUrl}/api/categories/{entity.CategoryId}");
    }
}

builder.Services.AddHateoasLinkProvider<Product, Guid, ProductLinkProvider>();

Select Operations

Expose only the operations you want, and mix custom endpoints with generated ones:

app.MapRestLib<Category, Guid>("/api/categories", config =>
{
    config.IncludeOperations(RestLibOperation.GetAll, RestLibOperation.GetById);
});

app.MapPost("/api/categories", async (Category category, IRepository<Category, Guid> repo) =>
{
    return Results.Created($"/api/categories/{category.Id}", await repo.CreateAsync(category));
});

You can also move this declarative resource configuration out of Program.cs and into JSON while keeping your model, repository, and hooks strongly typed:

{
  "RestLib": {
    "Resources": {
      "Products": {
        "Name": "products",
        "Route": "/api/products",
        "AllowAnonymousAll": true,
        "Operations": {
          "Exclude": ["Delete"]
        },
        "Filtering": ["CategoryId", "IsActive"],
        "Sorting": ["Price", "Name", "CreatedAt"],
        "DefaultSort": "name:asc",
        "OpenApi": {
          "Tag": "Product",
          "Summaries": {
            "GetAll": "List products"
          }
        }
      }
    }
  }
}
var productResource = builder.Configuration
    .GetSection("RestLib:Resources:Products")
    .Get<RestLibJsonResourceConfiguration>()!;

builder.Services.AddJsonResource<Product, Guid>(productResource);

var app = builder.Build();
app.MapJsonResources();

Extensible via Hooks

Inject custom logic into the pipeline without subclassing framework types:

app.MapRestLib<Product, Guid>("/api/products", config =>
{
    config.UseHooks(hooks =>
    {
        hooks.BeforePersist = ctx =>
        {
            if (ctx.Entity is Product product && ctx.Operation == RestLibOperation.Create)
            {
                product.CreatedAt = DateTime.UtcNow;
            }

            return Task.CompletedTask;
        };
    });
});

If you want a cleaner startup file, JSON config can select named hooks per operation while the hook implementations stay in C#:

builder.Services.AddNamedHook<Product, Guid>(HookNames.SetUpdatedAt, ctx =>
{
    if (ctx.Entity is Product product)
    {
        product.UpdatedAt = ctx.Operation == RestLibOperation.Create ? null : DateTime.UtcNow;
    }

    return Task.CompletedTask;
});
{
  "Hooks": {
    "BeforePersist": {
      "ByOperation": {
        "Create": ["SetUpdatedAt"],
        "Update": ["SetUpdatedAt"],
        "Patch": ["SetUpdatedAt"]
      }
    }
  }
}

This keeps route, auth, filtering, operation selection, OpenAPI metadata, and hook selection in JSON while your actual behavior remains strongly typed and testable in C#. A simple pattern is to centralize hook names in a HookNames class and use those constants when registering handlers.

Persistence-Agnostic

Use the in-memory adapter or plug in your own repository implementation:

public class ProductRepository : IRepository<Product, Guid>
{
    private readonly MyDbContext _db;

    public ProductRepository(MyDbContext db)
    {
        _db = db;
    }

    public async Task<Product?> GetByIdAsync(Guid id, CancellationToken ct)
        => await _db.Products.FindAsync([id], ct);

    // Implement the remaining IRepository members...
}

builder.Services.AddRepository<Product, Guid, ProductRepository>();

EF Core Adapter

Use the official EF Core adapter instead of writing a custom repository:

builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlite("Data Source=app.db"));

builder.Services.AddRestLibEfCore<AppDbContext, Product, Guid>();

The adapter auto-detects the primary key from EF Core model metadata. To customize options:

builder.Services.AddRestLibEfCore<AppDbContext, Product, Guid>(options =>
{
    options.KeySelector = p => p.Id;
    options.UseAsNoTracking = false;
});

The EF Core adapter supports RestLib's filtering, sorting, counting, pagination, batch operations, and hooks on top of EF Core, with server-side query translation for filtering, sorting, and counting. Field selection can also be pushed down to SQL when projection pushdown is enabled and the request only uses projectable direct scalar properties. Some capabilities have important implementation limits; see Current EF Core Adapter Limitations and ADR-021.

Current EF Core Adapter Limitations
  • Single-property keys only - the adapter auto-detects or accepts a single TKey value. Composite EF Core keys are not supported.
  • Keyset pagination with offset fallback - the EF Core adapter uses last-seen sort values plus the key for supported stable sorts, but falls back to encoded offset cursors for unsupported sort shapes.
  • Projection pushdown is opt-in and conditional - when enabled, EF Core pushes down direct scalar field selections and still includes key/filter/sort columns in SQL. Requests fall back to post-fetch field stripping for non-projectable fields or when HATEOAS, ETag, or hooks are active.
  • Top-level properties only - filtering, sorting, field selection, and PATCH handling operate on direct entity properties. Nested or related-property paths are not supported.
  • Constraint mapping is provider-limited - database constraint classification still relies primarily on exception-message inspection and is not yet specialized per provider.

Use the adapter when you want the standard RestLib endpoint surface over a typical EF Core model, but expect to write a custom repository if you need composite-key resources, deep/navigational query semantics, or SQL-level projection beyond direct scalar property selection.

Versioning

RestLib integrates with any ASP.NET Core versioning strategy via route groups.

URL prefix versioning
var v1 = app.MapGroup("/api/v1");
var v2 = app.MapGroup("/api/v2");

v1.MapRestLib<Product, Guid>("/products", cfg =>
{
    cfg.AllowAnonymous();
    cfg.ExcludeOperations(RestLibOperation.Patch, RestLibOperation.Delete);
    cfg.AllowFiltering(p => p.CategoryId);
});

v2.MapRestLib<Product, Guid>("/products", cfg =>
{
    cfg.AllowAnonymous();
    cfg.AllowFiltering(p => p.CategoryId, p => p.IsActive);
    cfg.AllowSorting(p => p.Price, p => p.Name);
    cfg.AllowFieldSelection(p => p.Id, p => p.Name, p => p.Price);
});
Prefix-less overload on a route group

When the route group already has the full path configured, use the prefix-less overload:

app.MapGroup("/api/v1/products").MapRestLib<Product, Guid>(cfg =>
{
    cfg.AllowAnonymous();
});
With Asp.Versioning.Http
// Install: Asp.Versioning.Http
builder.Services.AddApiVersioning();

var versionedApi = app.NewVersionedApi("Products");

versionedApi
    .MapGroup("/api/v{version:apiVersion}/products")
    .HasApiVersion(1.0)
    .MapRestLib<Product, Guid>(cfg => cfg.AllowAnonymous());

versionedApi
    .MapGroup("/api/v{version:apiVersion}/products")
    .HasApiVersion(2.0)
    .MapRestLib<Product, Guid>(cfg =>
    {
        cfg.AllowAnonymous();
        cfg.AllowFieldSelection(p => p.Id, p => p.Name, p => p.Price);
    });

RestLib does not depend on Asp.Versioning.Http — install it only if you need query-string, header, or media-type versioning strategies.

Performance

RestLib adds minimal overhead compared to hand-written Minimal APIs. In some cases, it is faster due to optimized code paths.

Operation Raw API RestLib Overhead Memory
GET by ID 170.5 us 217.4 us +27% +4%
GET all 313.4 us 271.7 us -13% +14%
POST 265.5 us 384.8 us +45% +13%
PUT 232.1 us 282.0 us +22% +13%

Benchmarks were run on .NET 10.0.5 with 100 seeded items on Linux (Ubuntu 24.04). Absolute times are higher than typical due to running without process priority elevation — focus on the relative overhead (Ratio column) rather than raw microseconds. Re-run with cd benchmarks/RestLib.Benchmarks && dotnet run -c Release to get numbers for your hardware.

<details> <summary>Full benchmark results</summary>

BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.4 LTS (Noble Numbat)
Intel Core i3-8130U CPU 2.20GHz (Kaby Lake), 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.201
  [Host]     : .NET 10.0.5 (10.0.5, 10.0.526.15411), X64 RyuJIT x86-64-v3
  DefaultJob : .NET 10.0.5 (10.0.5, 10.0.526.15411), X64 RyuJIT x86-64-v3

| Method                                      | Categories     | Mean     | Error    | StdDev    | Median   | Ratio | RatioSD | Allocated | Alloc Ratio |
|-------------------------------------------- |--------------- |---------:|---------:|----------:|---------:|------:|--------:|----------:|------------:|
| 'Raw Minimal API - POST'                    | Create         | 265.5 us | 30.76 us |  89.74 us | 260.1 us |  1.12 |    0.56 |  12.46 KB |        1.00 |
| 'RestLib - POST'                            | Create         | 384.8 us | 71.82 us | 203.73 us | 326.7 us |  1.63 |    1.07 |  14.10 KB |        1.13 |
|                                             |                |          |          |           |          |       |         |           |             |
| 'Raw Minimal API - GET all'                 | GetAll         | 313.4 us | 39.61 us | 111.73 us | 285.8 us |  1.12 |    0.54 |  16.74 KB |        1.00 |
| 'RestLib - GET all'                         | GetAll         | 271.7 us | 31.49 us |  92.86 us | 246.1 us |  0.97 |    0.46 |  19.05 KB |        1.14 |
|                                             |                |          |          |           |          |       |         |           |             |
| 'RestLib - GET all (no fields)'             | GetAll_Fields  | 289.6 us | 41.97 us | 121.78 us | 261.4 us |  1.18 |    0.70 |  19.07 KB |        1.00 |
| 'RestLib - GET all (?fields=id,name)'       | GetAll_Fields  | 475.3 us | 51.86 us | 149.62 us | 448.8 us |  1.93 |    0.99 |  39.98 KB |        2.10 |
| 'RestLib - GET all (?fields=id,name,price)' | GetAll_Fields  | 563.9 us | 88.23 us | 255.96 us | 498.5 us |  2.29 |    1.42 |  43.43 KB |        2.28 |
|                                             |                |          |          |           |          |       |         |           |             |
| 'Raw Minimal API - GET by ID'               | GetById        | 170.5 us | 26.94 us |  79.43 us | 149.6 us |  1.26 |    0.93 |   9.76 KB |        1.00 |
| 'RestLib - GET by ID'                       | GetById        | 217.4 us | 29.76 us |  82.47 us | 203.9 us |  1.61 |    1.07 |  10.13 KB |        1.04 |
|                                             |                |          |          |           |          |       |         |           |             |
| 'RestLib - GET by ID (no fields)'           | GetById_Fields | 124.3 us | 14.85 us |  42.84 us | 112.1 us |  1.12 |    0.55 |  10.21 KB |        1.00 |
| 'RestLib - GET by ID (?fields=id,name)'     | GetById_Fields | 162.7 us | 16.38 us |  46.47 us | 149.7 us |  1.46 |    0.65 |  12.22 KB |        1.20 |
|                                             |                |          |          |           |          |       |         |           |             |
| 'Raw Minimal API - PUT'                     | Update         | 232.1 us | 24.69 us |  72.81 us | 221.1 us |  1.10 |    0.51 |  12.78 KB |        1.00 |
| 'RestLib - PUT'                             | Update         | 282.0 us | 37.88 us | 108.06 us | 258.5 us |  1.34 |    0.69 |  14.44 KB |        1.13 |

</details>

Learn More

Architecture Decisions

Key decisions are documented as Architecture Decision Records:

ADR Decision
ADR-001 Cursor-based pagination over offset
ADR-002 Authorization required by default
ADR-003 Minimal APIs over controllers
ADR-004 snake_case JSON naming
ADR-005 RFC 9457 Problem Details for errors
ADR-006 Operation allowlists and denylists
ADR-007 Hybrid field projection strategy
ADR-008 Batch operations with partial success
ADR-009 Allow-list sorting with default sort
ADR-010 API versioning via route groups
ADR-011 Query parameter filtering
ADR-012 Hook pipeline for extensibility
ADR-013 Filter operators beyond equality
ADR-014 ETag support for caching and concurrency
ADR-015 Data Annotation validation
ADR-016 JSON resource configuration
ADR-017 Rate limiting integration
ADR-018 PATCH JsonElement coupling acknowledgement
ADR-019 HATEOAS hypermedia links
ADR-020 Structured logging
ADR-021 EF Core repository adapter

Packages

Package Description NuGet
RestLib Core library RestLib
RestLib.InMemory In-memory repository for testing and prototyping RestLib.InMemory
RestLib.EntityFrameworkCore EF Core repository adapter for production databases RestLib.EntityFrameworkCore

Requirements

  • .NET 10.0 or later
  • ASP.NET Core Minimal APIs

Known Limitations

  • Forward-only cursor pagination — cursors support forward traversal only; there is no backward/previous-page navigation.

  • Cursor contract, adapter-specific implementation — RestLib exposes an opaque cursor API. The InMemory adapter still uses encoded offsets/indexes, while the EF Core adapter uses keyset cursors for supported stable sorts and falls back to offsets otherwise.

  • Post-fetch field selection — field projection is applied after the full entity is retrieved from the repository, not pushed down to the data source.

  • Flat properties only — filtering, sorting, and field selection operate on top-level entity properties; nested or related entity paths are not supported.

  • No built-in search — full-text or fuzzy search is not included; implement it in your repository if needed.

  • No CORS configuration — RestLib does not configure CORS. If your API is consumed by browsers, add ASP.NET Core's built-in CORS middleware:

    builder.Services.AddCors(options =>
    {
        options.AddDefaultPolicy(policy => policy
            .AllowAnyOrigin()
            .AllowAnyMethod()
            .AllowAnyHeader());
    });
    app.UseCors();
    

    See the ASP.NET Core CORS documentation for production-ready policies.

Contributing

Contributions are welcome. Read the contributing guide.

License

This project is licensed under the MIT License. See the license.

Acknowledgments

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.5.2 93 5/27/2026
2.5.1 94 5/18/2026
2.5.0 108 5/13/2026
2.4.0 91 5/13/2026
2.3.0 113 4/16/2026
2.2.1 105 4/12/2026
2.2.0 107 4/11/2026
2.1.0 104 4/11/2026
2.0.0 108 4/10/2026
1.3.1 101 4/6/2026
1.3.0 101 4/4/2026
1.2.0 103 4/4/2026
1.1.0 106 4/4/2026
1.0.0 102 4/4/2026
0.8.0 104 4/1/2026
0.7.0 106 3/31/2026
0.6.0 103 3/30/2026
0.5.0 102 3/29/2026
0.4.0 101 3/26/2026
0.3.0 112 3/11/2026
Loading failed