Skip to main content
Version: 0.5.x

AutoCrud

Sharkable provides automatic CRUD API generation via IAutoCrudEntity<T>. Implement this marker interface on any ISharkEndpoint class, and safe CRUD operations are generated automatically — paginated by default, with zero additional configuration.

Why SqlSugar

Sharkable.AutoCrud is built on SqlSugar — a lightweight, high-performance ORM for .NET:

LicenseMIT — no restrictions, commercial-friendly
DatabasesMySQL, SQL Server, PostgreSQL, SQLite, Oracle, MariaDB, 达梦, 人大金仓, 瀚高, GaussDB, DuckDB, MongoDB, QuestDB, ClickHouse, OceanBase, DB2, HANA, TDSQL, ODBC
PerformanceClose to raw ADO.NET — faster than EF Core in benchmarks
AOT supportStaticConfig.EnableAot = true — compatible with .NET Native AOT
No tracking overheadQueries return plain POCO objects by default, zero change-tracking cost
LINQ + Lambda + SQLFull LINQ support with Where/OrderBy/Select, plus raw SQL when needed
Code-first / DB-firstAuto-migrate from entities or reverse-engineer from existing databases
Tenant supportBuilt-in multi-tenant table/DB isolation via SugarTenant
Split-table / split-dbHorizontal partitioning and sharding out of the box
Seed dataBuilt-in data seeder for test/demo environments

All of these work with Sharkable.AutoCrud without extra configuration — the generator maps IAutoCrudEntity<T> directly to SqlSugar's Queryable<T>/Insertable<T>/Updateable<T>/Deleteable<T> APIs.

Quick Start

1. Install Plugin

dotnet add package Sharkable.AutoCrud.SqlSugar

2. Configure Database

builder.Services.AddShark(opt =>
{
opt.ConfigureAutoCrud(s =>
{
s.DbType = DbType.Sqlite;
s.ConnectionString = "DataSource=app.db";
});
});

3. Define Entity + Endpoint

[SugarTable("products")]
public class Product
{
[SugarColumn(IsPrimaryKey = true)]
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}

public class ProductEndpoint : ISharkEndpoint, IAutoCrudEntity<Product>
{
// Empty — safe CRUD auto-generated (paginated, no full dump)
}

Generated routes at api/product:

MethodRouteOperationDescription
GET/Paginated list?page=1&pageSize=20{items,total,page,pageSize,totalPages}
GET/{id}Get by PKSingle entity
POST/CreateRequest body = entity
PUT/{id}UpdateRequest body = entity
DELETE/{id}DeleteBy PK

Pagination

List returns paginated results by default. No extra config needed:

GET /api/product?page=1&pageSize=20
{
"items": [{ "id": 1, "name": "Widget", "price": 9.99 }, ...],
"total": 847,
"page": 1,
"pageSize": 20,
"totalPages": 43
}

Default page size is 20; maximum is 100. Both are configurable via SqlSugarOptions:

builder.Services.AddShark(opt =>
{
opt.ConfigureAutoCrud(s =>
{
s.DbType = DbType.Sqlite;
s.ConnectionString = "DataSource=app.db";
s.MaxPageSize = 200;
s.DefaultPageSize = 50;
});
});

Unknown fields in query params are silently ignored.

Suppress Operations

Use AllowedOperations to selectively disable CRUD operations:

public class ReadOnlyEndpoint : ISharkEndpoint, IAutoCrudEntity<Product>
{
CrudOperations IAutoCrudEntity<Product>.AllowedOperations =>
CrudOperations.List | CrudOperations.Get;
// POST, PUT, DELETE suppressed
}

Available flags: None, List, Get, Create, Update, Delete, ListAll, All.

All = List | Get | Create | Update | Deletedoes not include ListAll. Full-table dumps must be explicitly opted into for safety.

Enable Full-Table Dump

CrudOperations IAutoCrudEntity<Product>.AllowedOperations =>
CrudOperations.All | CrudOperations.ListAll;
// Now GET /all returns the entire table

ListAll is intentionally excluded from All — full dumps are dangerous on large tables.

Custom Override

Write your own routes in AddRoutes() — they take precedence over auto-generated ones:

public class ProductEndpoint : ISharkEndpoint, IAutoCrudEntity<Product>
{
public void AddRoutes(IEndpointRouteBuilder app)
{
app.MapGet("/", async (ISqlSugarClient db) =>
{
var list = await db.Queryable<Product>()
.Where(p => p.Price > 0)
.OrderBy(p => p.Name)
.ToListAsync();
return Results.Ok(list);
});
}
// Get, Create, Update, Delete still auto-generated
}

Search & Filtering

The List operation supports filtering via filter[field][op]=value query parameters, plus sorting and pagination — all automatically, no code changes needed.

URL Convention

GET /api/product?filter[price][gte]=100&filter[price][lte]=500&filter[name][like]=Widget%&sort=-price&page=1&pageSize=20

Operators

KeySQLExample
(no op)=?filter[name]=Widget
eq=?filter[name][eq]=Widget
ne<>?filter[price][ne]=0
gt / gte> / >=?filter[price][gte]=100
lt / lte< / <=?filter[price][lte]=500
likeLIKE?filter[name][like]=Widget%
in / ninIN / NOT IN?filter[status][in]=active,pending
nullIS NULL?filter[deleted][null]=true

Sorting

?sort=field (asc), ?sort=-field (desc), ?sort=-price,+name (multi-field).

Safety

Unknown fields are silently ignored. All values are parameterized.

Frontend Example

const params = new URLSearchParams({
'filter[price][gte]': 100,
'filter[name][like]': 'Widget%',
sort: '-price'
});
fetch(`/api/product?${params}`);

Health Check

When EnableHealthChecks = true, SqlSugar connectivity is automatically checked via /healthz:

{
"checks": {
"SqlSugar": {
"status": "healthy",
"description": "SqlSugar connected in 3ms",
"data": { "latencyMs": 3, "dbType": "Sqlite" }
}
}
}

Soft Delete

Mark an entity with ISoftDeletable and AutoCrud handles soft deletion automatically — no code changes needed:

public class Product : ISoftDeletable
{
public int Id { get; set; }
public bool IsDeleted { get; set; } // convention-based detection
}

Behavior:

OperationWithout ISoftDeletableWith ISoftDeletable
List / Get / ListAllAll rowsWHERE IsDeleted = 0 only
DeleteHard deleteUPDATE SET IsDeleted = 1 (soft)

The IsDeleted property is detected by name (case-insensitive). No attributes, no configuration — just add the interface.

The field name is configurable via SqlSugarOptions if your entity uses a different name:

builder.Services.AddShark(opt =>
{
opt.ConfigureAutoCrud(s =>
{
s.SoftDeleteFieldName = "IsRemoved";
});
});
public class Product : ISoftDeletable
{
public int Id { get; set; }
public bool IsRemoved { get; set; } // custom field name
}

AOT Support (Zero rd.xml)

.NET Native AOT publishing requires all types to be known at compile time — the trimmer removes unreferenced types. Normally, SqlSugar entities need manual rd.xml entries to survive trimming.

Sharkable ships a Source Generator that discovers all IAutoCrudEntity<T> implementations and emits typeof(T) references for every entity type — forcing the trimmer to preserve them. No rd.xml needed.

dotnet publish -c Release -r linux-x64 --self-contained
# Entity types are preserved automatically

Works with any Sharkable.AutoCrud.SqlSugar version that depends on Sharkable ≥ 0.4.1.

Architecture

  • Sharkable core provides IAutoCrudEntity<T> + CrudOperations + IAutoCrudGenerator + FilterOperator
  • Sharkable.AutoCrud.SqlSugar implements IAutoCrudGenerator with SqlSugar ORM
  • AOT preservation via built-in Source Generator (zero config)
  • Future ORM plugins can implement IAutoCrudGenerator for other databases (EF Core, Dapper, etc.)