Trellis.EntityFrameworkCore
3.0.0-alpha.158
See the version list below for details.
dotnet add package Trellis.EntityFrameworkCore --version 3.0.0-alpha.158
NuGet\Install-Package Trellis.EntityFrameworkCore -Version 3.0.0-alpha.158
<PackageReference Include="Trellis.EntityFrameworkCore" Version="3.0.0-alpha.158" />
<PackageVersion Include="Trellis.EntityFrameworkCore" Version="3.0.0-alpha.158" />
<PackageReference Include="Trellis.EntityFrameworkCore" />
paket add Trellis.EntityFrameworkCore --version 3.0.0-alpha.158
#r "nuget: Trellis.EntityFrameworkCore, 3.0.0-alpha.158"
#:package Trellis.EntityFrameworkCore@3.0.0-alpha.158
#addin nuget:?package=Trellis.EntityFrameworkCore&version=3.0.0-alpha.158&prerelease
#tool nuget:?package=Trellis.EntityFrameworkCore&version=3.0.0-alpha.158&prerelease
EF Core Integration
Thin integration layer that eliminates repetitive EF Core boilerplate when using Trellis value objects and Result<T>.
Installation
dotnet add package Trellis.EntityFrameworkCore
Quick Start
Register all Trellis value objects as scalar properties with a single line in ConfigureConventions:
using Trellis.EntityFrameworkCore;
public class AppDbContext : DbContext
{
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
// Scans your assembly for CustomerId, OrderStatus, etc.
// Also auto-scans Trellis.Primitives for EmailAddress, Url, PhoneNumber, etc.
// Also auto-maps Money properties as owned types (Amount + Currency columns)
configurationBuilder.ApplyTrellisConventions(typeof(CustomerId).Assembly);
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// No HasConversion() boilerplate needed — just configure keys, indexes, constraints
modelBuilder.Entity<Customer>(b =>
{
b.HasKey(c => c.Id);
b.Property(c => c.Name).HasMaxLength(100).IsRequired();
b.Property(c => c.Email).HasMaxLength(254).IsRequired();
});
}
}
Money Properties — Zero Configuration
Money properties are automatically mapped as owned types with proper column naming and precision.
No OwnsOne calls needed — just declare Money properties on your entities and they work.
This also applies when Money is declared on owned entity types, including items inside OwnsMany collections.
Maybe<Money> is also supported — it auto-configures as an optional owned type with nullable Amount/Currency columns, no OwnsOne needed.
| Property Name | Amount Column | Currency Column | Amount Type | Currency Type |
|---|---|---|---|---|
Price |
Price |
PriceCurrency |
decimal(18,3) |
nvarchar(3) |
ShippingCost |
ShippingCost |
ShippingCostCurrency |
decimal(18,3) |
nvarchar(3) |
Maybe<T> Properties — Source Generator + Convention
Maybe<T> is a readonly struct that EF Core cannot map as optional. Use partial properties — the source generator and MaybeConvention handle everything:
public partial class Customer
{
public CustomerId Id { get; set; } = null!;
public partial Maybe<PhoneNumber> Phone { get; set; }
public partial Maybe<DateTime> SubmittedAt { get; set; }
}
No OnModelCreating configuration needed. When T is a composite owned type (e.g., Money), it creates an optional ownership navigation instead of a scalar column. Querying uses dedicated extensions:
var withoutPhone = await context.Customers.WhereNone(c => c.Phone).ToListAsync(ct);
var withPhone = await context.Customers.WhereHasValue(c => c.Phone).ToListAsync(ct);
var matches = await context.Customers.WhereEquals(c => c.Phone, phone).ToListAsync(ct);
var ordered = await context.Customers.WhereHasValue(c => c.Phone).OrderByMaybe(c => c.Phone).ToListAsync(ct);
Indexes, bulk updates, and diagnostics also stay strongly typed:
modelBuilder.Entity<Customer>(builder => builder.HasTrellisIndex(c => c.Phone));
await context.Customers
.Where(c => c.Id == customerId)
.ExecuteUpdateAsync(setters => setters.SetMaybeValue(c => c.Phone, phone), ct);
var mappings = context.GetMaybePropertyMappings();
var debugView = context.ToMaybeMappingDebugString();
Result-Returning SaveChanges
// Returns Result<int> instead of throwing on conflicts or FK violations
var result = await context.SaveChangesResultAsync(ct);
// Returns Result<Unit> when you don't need the count
var result = await context.SaveChangesResultUnitAsync(ct);
| Exception | Error Type |
|---|---|
DbUpdateConcurrencyException |
ConflictError |
| Duplicate key (unique constraint) | ConflictError |
| Foreign key violation | DomainError |
Query Extensions
// Maybe-returning queries (no exception on missing)
Maybe<Customer> customer = await context.Customers
.FirstOrDefaultMaybeAsync(c => c.Id == customerId, ct);
// Result-returning queries
Result<Customer> customer = await context.Customers
.FirstOrDefaultResultAsync(
c => c.Id == customerId,
Error.NotFound("Customer", customerId),
ct);
// Specification pattern
var activeSpec = new ActiveCustomerSpec();
var activeCustomers = await context.Customers
.Where(activeSpec)
.ToListAsync(ct);
Related Packages
- Trellis.Results — Core
Result<T>andMaybe<T>types - Trellis.Primitives — Value object base classes and built-in types
- Trellis.DomainDrivenDesign —
Specification<T>,Entity<T>,Aggregate<T>
License
MIT — see LICENSE for details.
| Product | Versions 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. |
-
net10.0
- Microsoft.EntityFrameworkCore.Relational (>= 10.0.5)
- Trellis.Primitives (>= 3.0.0-alpha.158)
- Trellis.Results (>= 3.0.0-alpha.158)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Trellis.EntityFrameworkCore:
| Package | Downloads |
|---|---|
|
Trellis.ServiceDefaults
Opinionated service composition defaults for Trellis web services. Provides a tiered builder that wires ASP integration, Mediator behaviors, FluentValidation, resource authorization, actor providers, and EF Core Unit of Work in canonical order. |
|
|
Trellis.EntityFrameworkCore.Outbox
Transactional outbox for Trellis. Atomically captures aggregate domain events to an EF Core table in the same transaction as the aggregate change, then relays them to Trellis domain-event handlers — durable, at-least-once, in-process dispatch. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.0.0-alpha.385 | 55 | 6/15/2026 |
| 3.0.0-alpha.382 | 60 | 6/12/2026 |
| 3.0.0-alpha.372 | 51 | 6/10/2026 |
| 3.0.0-alpha.360 | 71 | 6/7/2026 |
| 3.0.0-alpha.342 | 74 | 6/5/2026 |
| 3.0.0-alpha.337 | 63 | 6/3/2026 |
| 3.0.0-alpha.336 | 51 | 6/3/2026 |
| 3.0.0-alpha.304 | 69 | 5/29/2026 |
| 3.0.0-alpha.158 | 110 | 4/5/2026 |
| 3.0.0-alpha.157 | 71 | 4/4/2026 |
| 3.0.0-alpha.140 | 74 | 3/30/2026 |
| 3.0.0-alpha.137 | 74 | 3/27/2026 |
| 3.0.0-alpha.135 | 59 | 3/26/2026 |
| 3.0.0-alpha.127 | 64 | 3/23/2026 |
| 3.0.0-alpha.123 | 67 | 3/19/2026 |
| 3.0.0-alpha.118 | 72 | 3/14/2026 |
| 3.0.0-alpha.111 | 64 | 3/12/2026 |
| 3.0.0-alpha.104 | 60 | 3/9/2026 |
| 3.0.0-alpha.100 | 62 | 3/4/2026 |
| 3.0.0-alpha.99 | 59 | 3/4/2026 |