SkillAgentSearch skills...

Facet

Generate DTOs, mappings, constructors and LINQ projections from your domain models.

Install / Use

/learn @Tim-Maes/Facet
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

<div align="center"> <img src="https://raw.githubusercontent.com/Tim-Maes/Facet/master/assets/Facet.png" alt="Facet logo" width="400"> </div> <div align="center"> "One part of a subject, situation, object that has many parts." </div> <br> <div align="center">

CI Test CD NuGet Downloads GitHub Discord

</div>

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

: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 to string or int with full round-trip support
  • GenerateCopyConstructor - generate a copy constructor for cloning and MVVM scenarios
  • GenerateEquality - generate value-based Equals, 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

View on GitHub
GitHub Stars1.1k
CategoryDevelopment
Updated2d ago
Forks51

Languages

C#

Security Score

100/100

Audited on Apr 5, 2026

No findings