一键导入
koan-entity-first
Entity<T> patterns, GUID v7 auto-generation, static methods vs manual repositories
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Entity<T> patterns, GUID v7 auto-generation, static methods vs manual repositories
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Auto-registration via KoanAutoRegistrar, minimal Program.cs, "Reference = Intent" pattern
Chat endpoints, embeddings, RAG workflows, vector search
Aggregate boundaries, relationships, lifecycle hooks, value objects
Transparent L1/L2 caching for Entity<T>, [Cacheable] attribute, cross-node coherence, per-request opt-out
Run a mandatory pre-implementation exploration workflow before writing production code in Koan (.NET/C#). Use when a task requires code changes and Codex must first map concerns/layers, read relevant files and docs, check existing constants and types, identify the closest existing pattern, plan exact code placement, and confirm architectural guardrails.
EntityController<T>, custom routes, payload transformers, auth policies
| name | koan-entity-first |
| description | Entity<T> patterns, GUID v7 auto-generation, static methods vs manual repositories |
Entity replaces manual repositories. Every entity is self-aware and self-persisting. This pattern eliminates repository boilerplate while maintaining provider transparency.
await entity.Save(), await entity.Remove()await Entity.All(), await Entity.Get(id), await Entity.Query()// Create entity
public class Todo : Entity<Todo>
{
public string Title { get; set; } = "";
public bool Completed { get; set; }
// Id automatically generated as GUID v7 on first access
}
// Save (create or update)
var todo = new Todo { Title = "Buy milk" };
await todo.Save();
// Retrieve by ID
var loaded = await Todo.Get(id);
// Query all
var allTodos = await Todo.All();
// Filter with LINQ
var completed = await Todo.Query(t => t.Completed);
// Remove
await todo.Remove();
// For non-GUID keys: Entity<T, TKey>
public class NumericEntity : Entity<NumericEntity, int>
{
public override int Id { get; set; }
public string Title { get; set; } = "";
}
// Manual key management
var entity = new NumericEntity { Id = 42, Title = "Meaningful" };
await entity.Save();
// ✅ EFFICIENT: Single bulk query with IN clause
var ids = new[] { id1, id2, id3, id4 };
var todos = await Todo.Get(ids, ct);
// Result: [Todo?, Todo?, null, Todo?] - preserves order, null for missing
// ❌ INEFFICIENT: N database round-trips
var todos = new List<Todo?>();
foreach (var id in ids)
{
todos.Add(await Todo.Get(id, ct)); // N queries!
}
Use Cases:
Performance: Single query vs N queries = 10-100x faster for typical datasets
// Bulk save - efficient provider-specific batching
var todos = Enumerable.Range(0, 1000)
.Select(i => new Todo { Title = $"Task {i}" })
.ToList();
await todos.Save();
// Batch operations — returns IBatchSet<Todo, Guid>, committed with SaveAsync()
// Atomicity: relational providers wrap all operations in a transaction;
// document providers (Mongo) execute sequentially (no distributed tx by default).
// Size guidance: keep batches under ~1 000 items to stay within provider and memory limits.
IBatchSet<Todo, Guid> batch = Todo.Batch();
await batch
.Add(new Todo { Title = "New task" })
.Update(existingId, todo => todo.Completed = true)
.Delete(oldId)
.SaveAsync();
// Basic pagination
var page = await Todo.Page(pageNumber: 1, pageSize: 20);
// With total count for UI
var result = await Todo.QueryWithCount(
t => t.ProjectId == projectId,
new DataQueryOptions(
orderBy: nameof(Todo.Created),
descending: true
),
ct);
Console.WriteLine($"Showing {result.Items.Count} of {result.TotalCount}");
// Stream to avoid loading everything into memory
await foreach (var todo in Todo.AllStream(batchSize: 1000, ct))
{
// Process in batches - memory-efficient
await ProcessTodo(todo);
}
// Stream with filter
await foreach (var reading in Reading.QueryStream(
"plot == 'A1'",
batchSize: 200,
ct))
{
await ProcessReading(reading);
}
When to Stream: Large datasets (>10k records), background jobs, ETL pipelines
Entities can declare [Timestamp] properties for automatic time tracking:
[Timestamp] — set once (creation time)[Timestamp(OnSave = true)] — refreshed on every saveWorks with batch operations (UpsertMany) — all entities in the batch receive the current timestamp.
// DON'T create repository interfaces
public interface ITodoRepository
{
Task<Todo> GetAsync(string id);
Task SaveAsync(Todo todo);
Task<List<Todo>> GetAllAsync();
}
// DON'T inject repositories
public class TodoService
{
private readonly ITodoRepository _repo; // Unnecessary!
public TodoService(ITodoRepository repo) => _repo = repo;
}
Why wrong? Entity already provides all repository functionality. Manual repositories:
// Business logic services use Entity<T> directly
public class TodoService
{
public async Task<Todo> CompleteAsync(string id)
{
var todo = await Todo.Get(id); // Direct entity usage
if (todo is null)
throw new InvalidOperationException("Todo not found");
todo.Completed = true;
return await todo.Save(); // Instance save method
}
public async Task<List<Todo>> GetCompletedAsync()
{
return await Todo.Query(t => t.Completed); // Static query
}
}
Invoke this skill when:
// Default: framework chooses best strategy (usually optimized)
var total = await Todo.Count;
// Explicit exact count (guaranteed accuracy, may be slower)
var exact = await Todo.Count.Exact(ct);
// Explicit fast count (metadata estimate, extremely fast)
var fast = await Todo.Count.Fast(ct);
// Filtered count
var completed = await Todo.Count.Where(t => t.Completed);
Performance: Fast counts use database metadata (1000-20000x faster on large tables)
pg_stat_user_tables (~5ms vs 25s for 10M rows)sys.dm_db_partition_stats (~1ms vs 20s)estimatedDocumentCount() (~10ms vs 15s)When to Use:
examples/entity-crud.cs - Complete CRUD patternsexamples/entity-relationships.cs - Navigation helpersexamples/batch-operations.cs - Bulk loading and savinganti-patterns/manual-repositories.md - What NOT to do with detailed explanationsdocs/guides/entity-capabilities-howto.mddocs/guides/data-modeling.mdsamples/S1.Web/ (Relationship patterns)samples/S0.ConsoleJsonRepo/ (Minimal 20-line example)Entity patterns are mandatory in Koan Framework. Manual repositories break:
Always prefer Entity patterns over custom data access abstractions.