Dekiru.Internals.ApiUtils
2.0.2
Prefix Reserved
dotnet add package Dekiru.Internals.ApiUtils --version 2.0.2
NuGet\Install-Package Dekiru.Internals.ApiUtils -Version 2.0.2
<PackageReference Include="Dekiru.Internals.ApiUtils" Version="2.0.2" />
<PackageVersion Include="Dekiru.Internals.ApiUtils" Version="2.0.2" />
<PackageReference Include="Dekiru.Internals.ApiUtils" />
paket add Dekiru.Internals.ApiUtils --version 2.0.2
#r "nuget: Dekiru.Internals.ApiUtils, 2.0.2"
#:package Dekiru.Internals.ApiUtils@2.0.2
#addin nuget:?package=Dekiru.Internals.ApiUtils&version=2.0.2
#tool nuget:?package=Dekiru.Internals.ApiUtils&version=2.0.2
Dekiru.ApiUtils
A comprehensive ASP.NET Core utility library that provides standardised patterns for error handling, data access, row-level security, authentication, and more — so your team can focus on business logic instead of boilerplate.
Table of Contents
Installation
dotnet add package Dekiru.Internals.ApiUtils
Supported Frameworks
| Framework | Supported |
|---|---|
| .NET 8 | ✅ |
| .NET 9 | ✅ |
| .NET 10 | ✅ |
Features
Error Handling & ProblemDetails
Registers a global exception handler that converts unhandled exceptions into RFC 7807 ProblemDetails responses.
Setup
// Program.cs
builder.Services.AddDefaultProblemDetailsProducer();
var app = builder.Build();
app.UseGlobalExceptionHandler(); // optionally pass showFullCallstack: true in Development
Throwing Typed Errors
ProblemDetailsException provides factory helpers for the most common HTTP error scenarios:
// 404 Not Found
throw ProblemDetailsException.NotFound("Order not found.");
// 400 Bad Request
throw ProblemDetailsException.BadRequest("Invalid date range.");
// 409 Conflict
throw ProblemDetailsException.Conflict("Duplicate entry.");
// 403 Forbidden
throw ProblemDetailsException.Forbidden();
// 410 Gone
throw ProblemDetailsException.Gone("Resource has been permanently removed.");
Attach arbitrary extensions for richer client-side error context:
throw ProblemDetailsException.NotFound("Product not found.")
.WithExtensions(new Dictionary<string, object?> { ["productId"] = id });
Producing ProblemDetails Directly
return ProblemDetails.NotFound()
.WithDetail("The requested resource could not be found.")
.WithExtension("traceId", Activity.Current?.Id);
Custom Error Producer
Implement IProblemDetailsProducer and register it to override the default behaviour:
public class MyErrorProducer : IProblemDetailsProducer
{
public ProblemDetails Create(HttpContext context, IExceptionHandlerPathFeature? ex, bool showFullCallstack)
{
// custom mapping logic
return ProblemDetails.InternalServerError().WithDetail(ex?.Error.Message);
}
}
builder.Services.AddCustomProblemDetailsProducer<MyErrorProducer>();
Generic Data Access (ContextService)
ContextServiceBase<TContext> wraps a DbContext and provides type-safe, async CRUD helpers with built-in pagination, predicate filtering, and automatic timestamp stamping.
Define a Service
public class AppContextService : ContextServiceBase<AppDbContext>
{
public AppContextService(AppDbContext context, IServiceProvider provider)
: base(context, provider) { }
}
Register It
builder.Services.AddContextService<AppContextService, AppDbContext>();
Common Operations
// Fetch a single entity by primary key
var order = await _service.Set<Order>()
.FindAsync<Order, Guid>(orderId);
// Create
await _service.CreateAsync(new Order { ... });
// Update (auto-stamps IUpdated.Updated)
await _service.SaveChangesAsync();
// Soft-delete (sets Deleted = true if ISoftDeletable, otherwise hard-deletes)
await _service.DeleteAsync(order);
Request Types
!!! IMPORTANT !!! The request types have been moved to the Dekiru.ApiGenerator.Abstractions package.
| Type | Purpose |
|---|---|
ListRequest<T> |
Filter, paginate, sort, and control EF tracking for list queries |
GetRequest<T> |
Fetch a single entity by key (supports up to 3 composite keys) |
CreateRequest<TPayload, TResult> |
Wrap a create payload |
UpdateRequest<TKey, TPayload, TResult> |
Wrap an update payload with key(s) |
DeleteRequest<TKey, TEntity> |
Wrap a delete by key(s) |
CountRequest<T> |
Count matching entities |
Domain Authorization (Row-Level Security)
DomainHandler<T> lets you declare per-entity read/update/delete predicates and create/update/delete authorization checks that are automatically enforced at the EF Core query filter level.
Define a Handler
public class OrderDomainHandler : DomainHandler<Order>
{
private readonly ICurrentUser _user;
public OrderDomainHandler(ICurrentUser user)
{
_user = user;
ReadPredicate = o => o.TenantId == _user.TenantId;
UpdatePredicate = o => o.TenantId == _user.TenantId && !o.IsLocked;
DeletePredicate = o => o.TenantId == _user.TenantId && o.IsDraft;
}
public override Task<AuthorizationResult> CheckCreateAsync(Order entity)
=> _user.HasPermission("orders.create") ? Pass() : Fail("Not allowed to create orders.");
}
Register & Apply Query Filters
// Register all handlers in DI
builder.Services.AddScoped<DomainHandler<Order>, OrderDomainHandler>();
// In your DbContext.OnModelCreating, apply global query filters
provider.GetRequiredService<DomainHandlerProvider>().RegisterQueryFilters(modelBuilder);
Passthrough Handler
Use PassthroughDomainHandler<T> for entities that need no restriction — it permits all operations and applies no filter.
Entity Interfaces
Implement these interfaces on your EF entities to opt in to automatic behaviour provided by ContextServiceBase.
public class Order : ICreated, IUpdated, ISoftDeletable, IRowVersioned
{
public Guid Id { get; set; }
// Auto-set on first SaveChangesAsync
public DateTimeOffset Created { get; set; }
// Auto-set on every SaveChangesAsync
public DateTimeOffset Updated { get; set; }
// DeleteAsync sets this to true instead of removing the row
public bool Deleted { get; set; }
// Concurrency token — use with RowVersion helpers
public byte[] RowVersion { get; set; } = [];
}
| Interface | Behaviour |
|---|---|
ICreated |
Created is stamped once on the first save |
IUpdated |
Updated is stamped on every save |
ISoftDeletable |
DeleteAsync sets Deleted = true rather than removing the row |
IRowVersioned |
Enables optimistic concurrency via HasVersion() and RowVersion helpers |
JSON & Service Configuration
A single extension method configures all recommended JSON serialiser settings:
builder.Services.ConfigureJson();
// Applies: camelCase property naming, ignore null values,
// ignore reference cycles, string enum converter,
// byte[] ↔ Base64 row-version converter
Register the row-version converter independently if needed:
builder.Services.AddRowVersionJsonConverter();
Azure Easy Auth
Parses the x-ms-client-principal header injected by Azure App Service / Container Apps Easy Auth into a standard ClaimsPrincipal.
builder.Services.AddEasyAuthAuthentication();
Use it the same way as any other ASP.NET Core authentication scheme — HttpContext.User is populated automatically. Extend EasyAuthHandler to customise claim transformation.
Row Versioning
Protect against lost-update conflicts with optimistic concurrency.
// In your EF model configuration
entity.Property(e => e.RowVersion).IsRowVersion();
// Validate incoming version from a client request
if (!entity.HasVersion(incomingVersion))
throw ProblemDetailsException.Conflict("The resource has been modified. Please refresh and retry.");
// Convert between byte[] and Base64 string (e.g. for API payloads)
string base64 = RowVersion.ToString(entity.RowVersion);
byte[] bytes = RowVersion.FromString(base64);
When AddRowVersionJsonConverter() (or ConfigureJson()) is registered, byte[] properties are automatically serialised as Base64 strings in all JSON responses.
SPA Middleware
Serve a Single Page Application with optional file-level content transformations (e.g. injecting environment variables into index.html at runtime).
app.UseSpaWithTransforms(
contentPath: Path.Combine(Directory.GetCurrentDirectory(), "wwwroot"),
config: (spa, values) =>
{
spa.Options.SourcePath = "ClientApp";
// values can be used to substitute placeholders in static files
});
Provide a StaticFileTransformerOptions instance for full control over caching headers, MIME types, and custom IFileProvider implementations.
License
MIT © Dekiru Solutions AB
LLM Quick Reference
This section is structured for AI code assistants. It provides a dense, scannable API surface so you can answer "how do I do X with this library?" without re-reading the full documentation above.
Library Mission
Dekiru.ApiUtils is a multi-targeted ASP.NET Core library (net8.0 / net9.0 / net10.0) that provides standardised, reusable infrastructure for REST APIs: RFC 7807 error responses, generic EF Core CRUD, row-level security, entity auditing, optimistic concurrency, Azure Easy Auth, JSON configuration, and SPA middleware.
Minimum Bootstrap (Program.cs)
// Error handling (required for ProblemDetailsException to produce HTTP responses)
builder.Services.AddDefaultProblemDetailsProducer();
// JSON (camelCase, null-ignore, cycle-safe, enum-as-string, row-version as Base64)
builder.Services.ConfigureJson();
// Domain authorization (row-level security via EF query filters)
builder.Services.AddDomainHandlers(); // scans calling assembly
// Context service (generic CRUD wrapper around DbContext)
builder.Services.AddContextService<AppContextService, AppDbContext>();
// Azure Easy Auth (optional — Azure App Service / Container Apps only)
builder.Services.AddEasyAuthAuthentication();
var app = builder.Build();
app.UseGlobalExceptionHandler(); // converts unhandled exceptions → ProblemDetails
Namespace Map
| Namespace | Contents |
|---|---|
Dekiru.ApiUtils.ErrorHandling |
ProblemDetailsException, ProblemDetails, Maybe<T>, GlobalExceptionHandler, IProblemDetailsProducer, DefaultProblemDetailsProducer |
Dekiru.ApiUtils.Services |
ContextServiceBase, ContextServiceBase<TContext>, all request types (ListRequest<T>, GetRequest, CreateRequest, UpdateRequest, DeleteRequest, CountRequest, LinkRequest, UnlinkRequest, ReplaceLinkRequest), Tracking enum |
Dekiru.ApiUtils.Domain |
DomainHandler<T>, DomainHandlerProvider, PassthroughDomainHandler<T>, DomainHandlerExtensions |
Dekiru.ApiUtils.Interfaces |
ICreated, IUpdated, ISoftDeletable, IRowVersioned |
Dekiru.ApiUtils.Extensions |
ConfigurationExtensions, EntityFrameworkCoreExtensions, GeneralExtensions, SimpleValidationExtensions, CyclesExtensions |
Dekiru.ApiUtils.Utils |
RowVersion, RowVersionJsonConverter, EasyAuthHandler, JsonProxy<T>, JsonProxyIgnoreAttribute, ValueSource |
Dekiru.ApiUtils.Spa |
SpaStaticWithTransformsExtensions, StaticFileTransformerOptions, ValueSource |
Scenario → API Reference
| I want to… | Use |
|---|---|
| Return a 404 from a controller/service | throw ProblemDetailsException.NotFound("message") |
| Return a 400 Bad Request | throw ProblemDetailsException.BadRequest("message") |
| Return a 409 Conflict | throw ProblemDetailsException.Conflict("message") |
| Return a 403 Forbidden | throw ProblemDetailsException.Forbidden() |
| Return a 410 Gone | throw ProblemDetailsException.Gone("message") |
| Attach extra data to an error response | .WithExtensions(new Dictionary<string, object?> { ["key"] = value }) |
| Convert exception handling to ProblemDetails globally | services.AddDefaultProblemDetailsProducer() + app.UseGlobalExceptionHandler() |
| Customise how exceptions map to responses | Implement IProblemDetailsProducer, register with services.AddCustomProblemDetailsProducer<T>() |
| Wrap a result that may be an error | Maybe<T> — use Map(), Bind(), Switch() for monadic chaining |
| Validate a required string | value.NotNullOrWhiteSpace() (throws ProblemDetailsException 400 on failure) |
| Validate a numeric range | value.InRange(min, max) |
| Validate enum membership | value.InEnum<TEnum>() |
| Validate against a regex | value.MatchRegex(pattern) |
| Fetch a list with pagination/filtering | await _service.ListAsync(new ListRequest<T> { Skip = n, Take = n, Filter = x => ..., Sorting = [...] }) |
| Fetch one entity by primary key | await _service.Set<T>().FindAsync<T, TKey>(key) |
| Create an entity (with auto-timestamps) | await _service.CreateAsync(entity) |
Update an entity (auto-stamps IUpdated) |
mutate entity, then await _service.SaveChangesAsync() |
| Soft-delete or hard-delete an entity | await _service.DeleteAsync(entity) — soft-deletes if ISoftDeletable, otherwise removes row |
| Count matching entities | await _service.CountAsync(new CountRequest<T> { Filter = x => ... }) |
| Apply row-level security to reads | Define ReadPredicate in a DomainHandler<T> subclass; register with AddDomainHandlers() |
| Authorize create/update/delete in a handler | Override CheckCreateAsync, CheckUpdateAsync, CheckDeleteAsync in DomainHandler<T> |
| Allow all operations (no restriction) | Register / use PassthroughDomainHandler<T> |
| Apply EF query filters from domain handlers | Call provider.GetRequiredService<DomainHandlerProvider>().RegisterQueryFilters(modelBuilder) in OnModelCreating |
| Detect optimistic concurrency conflict | if (!entity.HasVersion(incomingBytes)) throw ProblemDetailsException.Conflict(...) |
Serialize byte[] row version as Base64 |
builder.Services.AddRowVersionJsonConverter() (included in ConfigureJson()) |
| Convert Base64 string ↔ byte[] manually | RowVersion.ToString(bytes) / RowVersion.FromString(base64) |
| Parse Azure Easy Auth claims | builder.Services.AddEasyAuthAuthentication() — populates HttpContext.User |
Copy a JsonObject into a typed object |
new JsonProxy<T>(jsonObject).CopyTo(instance) |
| Serve SPA static files with runtime substitutions | app.UseSpaWithTransforms(contentPath, (spa, values) => { ... }) |
| Remove circular references from EF results | .CleanAsync() / .CleanFirstOrDefault<T>() / .CleanSingleOrDefault<T>() extension methods |
| Link two entities by ID | new LinkRequest<TSource, TTarget> { SourceId = ..., TargetId = ... } |
| Unlink two entities | new UnlinkRequest<TSource, TTarget> { SourceId = ..., TargetId = ... } |
| Replace all links from one source to many targets | new ReplaceLinkRequest<TSource, TTarget> { SourceId = ..., TargetIds = [...] } |
Complete Public Type Catalog
Interfaces (Dekiru.ApiUtils.Interfaces)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
ICreated |
interface | Auto-stamped creation timestamp | DateTimeOffset Created { get; set; } |
IUpdated |
interface | Auto-stamped update timestamp | DateTimeOffset Updated { get; set; } |
ISoftDeletable |
interface | Soft-delete flag | bool Deleted { get; set; } |
IRowVersioned |
interface | Optimistic concurrency token | byte[] RowVersion { get; set; } |
Error Handling (Dekiru.ApiUtils.ErrorHandling)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
ProblemDetailsException |
exception class | Throwable RFC 7807 error with HTTP status | .NotFound(), .BadRequest(), .Conflict(), .Forbidden(), .Gone(), .WithExtensions() |
ProblemDetails |
class | RFC 7807 domain model; buildable | .NotFound(), .BadRequest(), .WithDetail(), .WithExtension() |
Maybe<T> |
class | Success-or-error result monad | .Map(), .Bind(), .Switch(), .GetValueOrDefault(), implicit operators |
IProblemDetailsProducer |
interface | Factory for custom error mapping | ProblemDetails Create(HttpContext, IExceptionHandlerPathFeature?, bool) |
DefaultProblemDetailsProducer |
class | Default producer; virtual methods for customisation | virtual ProblemDetails OnUnhandledException(...) |
GlobalExceptionHandler |
static class | Middleware registration helpers | app.UseGlobalExceptionHandler(showFullCallstack?) |
Services (Dekiru.ApiUtils.Services)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
ContextServiceBase |
abstract class | Non-generic base; pre-save timestamp/soft-delete logic | static PreSaveCheck(DbContext) |
ContextServiceBase<TContext> |
abstract class | Generic CRUD base wrapping TContext : DbContext |
Set<T>(), ListAsync(), CreateAsync(), DeleteAsync(), SaveChangesAsync(), BeginTransactionAsync() |
Tracking |
enum | EF query tracking mode | Enabled, Disabled |
RequestBase<T> |
abstract class | Base for all query requests | Filter, Predicate |
ListRequest<T> |
class | List with pagination, sort, filter, tracking, includes | Skip, Take, Sorting, Filter, Tracking, Includes |
GetRequest<TKey, T> |
class | Fetch by single key | Key, Tracking, Includes |
GetRequest<TKey1, TKey2, T> |
class | Fetch by two-part composite key | Key1, Key2 |
GetRequest<TKey1, TKey2, TKey3, T> |
class | Fetch by three-part composite key | Key1, Key2, Key3 |
CreateRequest<TPayload, TResult> |
class | Wrap a create payload | Payload |
UpdateRequest<TPayload, TResult> |
class | Wrap update payload (key embedded in payload) | Payload |
UpdateRequest<TKey, TPayload, TResult> |
class | Wrap update with separate key | Key, Payload |
UpdateRequest<TKey1, TKey2, TPayload, TResult> |
class | Two-key update | Key1, Key2, Payload |
UpdateRequest<TKey1, TKey2, TKey3, TPayload, TResult> |
class | Three-key update | Key1, Key2, Key3, Payload |
DeleteRequest<TKey, TEntity> |
class | Delete by single key | Key |
DeleteRequest<TKey1, TKey2, TEntity> |
class | Delete by two-part key | Key1, Key2 |
DeleteRequest<TKey1, TKey2, TKey3, TEntity> |
class | Delete by three-part key | Key1, Key2, Key3 |
CountRequest<T> |
class | Count entities matching optional filter | Filter |
LinkRequest<TSource, TTarget> |
class | Link two entities (int IDs) | SourceId, TargetId |
LinkRequest<TSource, TTarget, TSourceKey, TTargetKey> |
class | Link with generic key types | SourceId, TargetId |
UnlinkRequest<TSource, TTarget> |
class | Unlink two entities (int IDs) | SourceId, TargetId |
UnlinkRequest<TSource, TTarget, TSourceKey, TTargetKey> |
class | Unlink with generic keys | SourceId, TargetId |
ReplaceLinkRequest<TSource, TTarget> |
class | Replace all target links from a source (int IDs) | SourceId, TargetIds |
ReplaceLinkRequest<TSource, TTarget, TSourceKey, TTargetKey> |
class | Replace links with generic keys | SourceId, TargetIds |
Domain Authorization (Dekiru.ApiUtils.Domain)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
DomainHandler<T> |
abstract class | Per-entity auth handler with predicates | ReadPredicate, UpdatePredicate, DeletePredicate; CheckCreateAsync(), CheckUpdateAsync(), CheckDeleteAsync(), Pass(), Fail() |
DomainHandlerProvider |
class | Resolves handlers from DI; applies query filters | RegisterQueryFilters(ModelBuilder), CheckCreateAsync<T>(), CheckUpdateAsync<T>(), CheckDeleteAsync<T>() |
PassthroughDomainHandler<T> |
class | No-op handler; allows all operations | (no configuration needed) |
Utils (Dekiru.ApiUtils.Utils)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
RowVersion |
static class | byte[] ↔ Base64 conversions |
ToString(byte[]), FromString(string) |
RowVersionJsonConverter |
class | System.Text.Json converter for row version |
Registered via AddRowVersionJsonConverter() |
EasyAuthHandler |
class | ASP.NET Core auth handler for Azure Easy Auth | Registered via AddEasyAuthAuthentication(); reads x-ms-client-principal header |
JsonProxy<T> |
class | Copies JsonObject properties to a typed instance |
CopyTo(T instance) |
JsonProxyIgnoreAttribute |
attribute | Excludes a property from JsonProxy<T> copying |
Apply to target type properties |
ValueSource |
class | Key-value store for SPA template substitution | this[string key] indexer |
SPA (Dekiru.ApiUtils.Spa)
| Type | Kind | Purpose | Key Member(s) |
|---|---|---|---|
SpaStaticWithTransformsExtensions |
static class | Middleware registration | app.UseSpaWithTransforms(contentPath, config) |
StaticFileTransformerOptions |
class | Extends StaticFileOptions with transform config |
FileMatchPredicate, CacheControl |
Entity Interface Decision Guide
| Requirement | Interface to implement |
|---|---|
| Record when a row was first created | ICreated |
| Record when a row was last modified | IUpdated |
| Hide deleted rows instead of removing them | ISoftDeletable |
| Detect and reject stale concurrent updates | IRowVersioned |
All four can be combined on the same entity. ContextServiceBase handles stamping and deletion automatically during SaveChangesAsync and DeleteAsync.
Key Conventions
| Convention | Detail |
|---|---|
| DI Lifetimes | ContextServiceBase<TContext>: scoped. DomainHandler<T>: scoped. DomainHandlerProvider: scoped. |
| Throwing errors | Always use ProblemDetailsException factory methods. Never throw HttpRequestException or return raw status codes. |
| Timestamp stamping | ContextServiceBase.PreSaveCheck() is called inside SaveChangesAsync. You do not call it manually. |
| Soft delete | DeleteAsync checks ISoftDeletable; if implemented, sets Deleted = true and calls SaveChangesAsync. Hard-deletes otherwise. |
| JSON output | After ConfigureJson(): property names are camelCase, null values are omitted, enums are strings, byte[] is Base64, object cycles are broken. |
| Row version on the wire | Serialised as a Base64 string. Use RowVersion.FromString() to convert back before calling HasVersion(). |
| Query filters | Domain handler ReadPredicate is registered as a global EF Core query filter in OnModelCreating. It applies to every query on that entity type automatically. |
| Validation methods | All SimpleValidationExtensions methods use [CallerArgumentExpression] to include the parameter name in the error message automatically. |
| Assembly scanning | AddDomainHandlers() (no args) scans the calling assembly. Use AddDomainHandlers<T>() or AddDomainHandlers(Assembly) to target a specific assembly. |
Component Wiring
The three main runtime components — DbContext, ContextServiceBase<TContext>, and DomainHandler<T> — connect as follows:
Registration (
Program.cs): CallAddContextService<TService, TContext>()andAddDomainHandlers(). Both are scoped to the HTTP request.Model creation (
DbContext.OnModelCreating): ResolveDomainHandlerProviderfrom the service provider and callRegisterQueryFilters(modelBuilder). This iterates every registeredDomainHandler<T>and attaches itsReadPredicateas an EF Core global query filter. The filter runs on everyDbSet<T>query for the lifetime of the context.Request handling: Inject your
ContextServiceBase<TContext>subclass. CallListAsync,CreateAsync,DeleteAsync, etc. Before any write,ContextServiceBaseinternally:- Calls
DomainHandlerProvider.CheckCreateAsync<T>()/CheckUpdateAsync<T>()/CheckDeleteAsync<T>()to enforce authorization. - Calls
PreSaveCheck(context)to stamp timestamps and handle soft-delete flags.
- Calls
Error flow: If any authorization check or validation fails, a
ProblemDetailsExceptionis thrown.UseGlobalExceptionHandler()middleware catches it and writes a RFC 7807 JSON body with the correct HTTP status code.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 is compatible. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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. |
-
net10.0
- Dekiru.ApiGenerator.Abstractions (>= 1.0.0)
- Microsoft.AspNetCore.SpaServices.Extensions (>= 10.0.8)
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.8)
-
net8.0
- Dekiru.ApiGenerator.Abstractions (>= 1.0.0)
- Microsoft.AspNetCore.SpaServices.Extensions (>= 8.0.27)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.16)
-
net9.0
- Dekiru.ApiGenerator.Abstractions (>= 1.0.0)
- Microsoft.AspNetCore.SpaServices.Extensions (>= 9.0.16)
- Microsoft.EntityFrameworkCore.Relational (>= 9.0.16)
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.0.2 | 105 | 5/19/2026 |
| 2.0.1 | 121 | 4/10/2026 |
| 2.0.0 | 106 | 4/10/2026 |
| 2.0.0-preview.1 | 58 | 4/9/2026 |
| 2.0.0-preview.0 | 85 | 4/1/2026 |
| 1.5.1 | 743 | 12/2/2025 |
| 1.5.0 | 205 | 11/24/2025 |
| 1.5.0-rc.7 | 162 | 11/22/2025 |
| 1.5.0-rc.6 | 366 | 11/19/2025 |
| 1.5.0-rc.5 | 372 | 11/19/2025 |
| 1.5.0-rc.4 | 370 | 11/19/2025 |
| 1.5.0-rc.3 | 375 | 11/18/2025 |
| 1.5.0-rc.2 | 372 | 11/18/2025 |
| 1.5.0-rc.1 | 368 | 11/18/2025 |
| 1.5.0-rc.0 | 360 | 11/17/2025 |
| 1.4.1 | 299 | 11/11/2025 |
| 1.4.0 | 164 | 11/8/2025 |
| 1.3.9 | 212 | 11/5/2025 |
| 1.3.8 | 210 | 11/4/2025 |
| 1.3.7 | 211 | 11/4/2025 |