Facet
Generate DTOs, mappings, constructors and LINQ projections from your domain models.
Install / Use
/learn @Tim-Maes/FacetREADME
Facet is a C# source generator that eliminates DTO boilerplate. Declare what you want, and Facet generates the type, constructor, LINQ projection, and reverse mapping, all at compile time with zero runtime overhead.
:gem: What is a Facet?
Think of your domain model as a gem with many facets! Different views for different purposes:
- Public APIs need a facet without sensitive data
- Admin endpoints need a different facet with additional fields
- Database queries need efficient projections
Instead of manually creating each facet, Facet auto-generates them from a single source of truth. Generates constructors, projections, reverse mappings, patch, etc...
:clipboard: Documentation
- Documentation & Guides
- Facet Dashboard
- What is being generated?
- Configure generated files output location
- Global configuration defaults - Override attribute defaults project-wide
- Comprehensive article about Facetting
:star: Features
Code Generation
- Generate DTOs as classes, records, structs, or record structs
- Constructor, static factory, and LINQ projection expression, all generated
- Nested objects and collections mapped automatically
- Preserves XML documentation and data validation attributes
Mapping & Customization
- Include/exclude properties with simple attribute arguments
- Global Configuration - override default attribute settings project-wide via MSBuild properties (docs)
[MapFrom]- declarative property renaming with optional reverse mapping and expression support[MapWhen]- conditional mapping based on runtime values, works in SQL projections- Before/After hooks - inject validation, defaults, or computed values around auto-mapping
ConvertEnumsTo- convert all enums tostringorintwith full round-trip supportGenerateCopyConstructor- generate a copy constructor for cloning and MVVM scenariosGenerateEquality- generate value-basedEquals,GetHashCode,==,!=for class DTOs- Sync and async custom mapping configurations (static or DI-resolved instances)
Advanced Features
[Flatten]- collapse nested object graphs into top-level properties[Wrapper]- reference-based delegation for facades, ViewModels, and decorators[GenerateDtos]- auto-generate full CRUD DTO sets (Create, Update, Response, Query, Upsert, Patch)- Source signature tracking - compile-time warning when a source entity's structure changes
- Inheritance - traverses base classes; suppresses duplicates when facets inherit base types
- Expression transformation - remap predicates and selectors from entity types to their projections
Integration
- Full Entity Framework Core support — automatic navigation loading, no
.Include()required - Works with any LINQ provider via
Facet.Extensions - Async EF Core variants with cancellation token support
- Custom async mappers with dependency injection for mappings that require I/O
- Supports .NET 8, .NET 9, and .NET 10
- Zero runtime cost, no reflection, everything generated at compile time
:rocket: Quick Start
<details> <summary>Installation</summary>Install the NuGet Package
dotnet add package Facet
For LINQ helpers:
dotnet add package Facet.Extensions
For EF Core support:
dotnet add package Facet.Extensions.EFCore
For advanced EF Core custom mappers (with DI support):
dotnet add package Facet.Extensions.EFCore.Mapping
For expression transformation utilities:
dotnet add package Facet.Mapping.Expressions
</details>
<details>
<summary>Define Facets</summary>
// Example domain models:
public class User
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string Email { get; set; }
public string PasswordHash { get; set; }
public DateTime DateOfBirth { get; set; }
public decimal Salary { get; set; }
public string Department { get; set; }
public bool IsActive { get; set; }
public Address HomeAddress { get; set; }
public Company Employer { get; set; }
public List<Project> Projects { get; set; }
public DateTime CreatedAt { get; set; }
public string InternalNotes { get; set; }
}
public class Address
{
public string Street { get; set; }
public string City { get; set; }
public string State { get; set; }
public string ZipCode { get; set; }
}
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public Address Headquarters { get; set; }
}
public class Project
{
public int Id { get; set; }
public string Name { get; set; }
public DateTime StartDate { get; set; }
}
Create focused facets for different scenarios:
// 1. Public API - Exclude all sensitive data
[Facet(typeof(User),
exclude: [nameof(User.PasswordHash), nameof(User.Salary), nameof(User.InternalNotes)])]
public partial record UserPublicDto;
// 2. Contact Information - Include only specific properties
[Facet(typeof(User),
Include = [nameof(User.FirstName), nameof(User.LastName), nameof(User.Email), nameof(User.Department)])]
public partial record UserContactDto;
// 3. Query/Filter DTO - Make all properties nullable
[Facet(typeof(User),
Include = [nameof(User.FirstName), nameof(User.LastName), nameof(User.Email), nameof(User.Department), nameof(User.IsActive)],
NullableProperties = true,
GenerateToSource = false)]
public partial record UserFilterDto;
// 4. Validation-Aware DTO - Copy data annotations
[Facet(typeof(User),
Include = [nameof(User.FirstName), nameof(User.LastName), nameof(User.Email)],
CopyAttributes = true)]
public partial record UserRegistrationDto;
// 5. Nested Objects - Single nested facet
[Facet(typeof(Address))]
public partial record AddressDto;
[Facet(typeof(User),
Include = [nameof(User.Id), nameof(User.FirstName), nameof(User.LastName), nameof(User.HomeAddress)],
NestedFacets = [typeof(AddressDto)])]
public partial record UserWithAddressDto;
// Address -> AddressDto automatically
// Type-safe nested mapping
// 6. Complex Nested - Multiple nested facets
[Facet(typeof(Company), NestedFacets = [typeof(AddressDto)])]
public partial record CompanyDto;
[Facet(typeof(User),
exclude: [nameof(User.PasswordHash), nameof(User.Salary), nameof(User.InternalNotes)],
NestedFacets = [typeof(AddressDto), typeof(CompanyDto)])]
public partial record UserDetailDto;
// Multi-level nesting supported
// 7. Collections - Automatic collection mapping
[Facet(typeof(Project))]
public partial record ProjectDto;
[Facet(typeof(User),
Include = [nameof(User.Id), nameof(User.FirstName), nameof(User.LastName), nameof(User.Projects)],
NestedFacets = [typeof(ProjectDto)])]
public partial record UserWithProjectsDto;
// List<Project> -> List<ProjectDto> automatically!
// Arrays, ICollection<T>, IEnumerable<T> all supported
// 8. Everything Combined
[Facet(typeof(User),
exclude: [nameof(User.PasswordHash), nameof(User.Salary), nameof(User.InternalNotes)],
NestedFacets = [typeof(AddressDto), typeof(CompanyDto), typeof(ProjectDto)],
CopyAttributes = true)]
public partial record UserCompleteDto;
// Excludes sensitive fields
// Maps nested Address and Company objects
// Maps Projects collection (List<Project> -> List<ProjectDto>)
// Copies validation attributes
// Ready for production APIs
</details>
<details>
<summary>Basic Projection of Facets</summary>
[Facet(typeof(User))]
public partial class UserFacet { }
// Map your source to facet
var userFacet = user.ToFacet<UserFacet>();
var userFacet = user.ToFacet<User, UserFacet>(); //Much faster
// Map back to source
var user = userFacet.ToSource<User>();
var user = userFacet.ToSource<UserFacet, User>(); //Much faster
// Patch only changed properties back to source
user.ApplyFacet(userFacet);
user.ApplyFacet<User, UserFacet>(userFacet); // Much faster
// Patch with change tracking
bool hasChanges = userFacet.ApplyFacetWithChanges<user, userDto>(userFacet);
// LINQ queries
var users = users.SelectFacets<UserFacet>();
var users = users.SelectFacets<User, UserFacet>(); //Much faster
</details>
<detai
Related Skills
node-connect
350.1kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
109.9kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
350.1kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
350.1kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
