"One part of a subject, situation, object that has many parts."
Facet is a C# source generator that lets you define lightweight projections (DTOs, API models, etc.) directly from your domain models — without writing boilerplate.
It generates partial classes, records structs or record structs with constructors, optional LINQ projections, and even supports custom mappings — all at compile time, with zero runtime cost.
Facetting is the process of defining focused views of a larger model at compile time.
Instead of manually writing separate DTOs, mappers, and projections, Facet allows you to declare what you want to keep — and generates everything else.
You can think of it like carving out a specific facet of a gem:
- The part you care about
- Leaving the rest behind.
- Reduce duplication across DTOs, projections, and ViewModels
- Maintain strong typing with no runtime cost
- Stay DRY (Don't Repeat Yourself) without sacrificing performance
- Works seamlessly with LINQ providers like Entity Framework
- ✅ Generate classes, records, structs or record structs from existing types
- ✅ Exclude fields/properties you don't want (create a Facetted view of your model))
- ✅ Include public fields (optional)
- ✅ Auto-generate constructors for fast mapping
- ✅ LINQ projection expressions
(Expression<Func<TSource,TTarget>>)
- ✅ Custom mapping via
IFacetMapConfiguration
dotnet add package Facet
using Facet;
public class Person
{
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
// Generate a DTO that excludes Email
[Facet(typeof(Person), exclude: nameof(Person.Email))]
public partial class PersonDto { }
This generates a PersonDto with only Name and Age, and a constructor that copies values.
var person = new Person { Name = "Alice", Email = "a@b.com", Age = 30 };
var dto = new PersonDto(person);
LINQ projection support:
var query = dbContext.People
.Select(PersonDto.Projection)
.ToList();
[Facet(typeof(Person), Kind = FacetKind.Record)]
public partial record PersonRecord;
[Facet(typeof(MyStruct), Kind = FacetKind.Struct, IncludeFields = true)]
public partial struct MyStructDto;
[Facet(typeof(MyRecordStruct), Kind = FacetKind.RecordStruct)]
public partial record struct MyRecordStructDto;
Eg. you need to compile extra fields
public class UserMapConfig : IFacetMapConfiguration<User, UserDto>
{
public static void Map(User source, UserDto target)
{
target.FullName = $"{source.FirstName} {source.LastName}";
}
}
[Facet(typeof(User), Configuration = typeof(UserMapConfig))]
public partial class UserDto;
This lets you add derived properties, formatting, etc.
Install Facet.Extensions for one-line mapping helpers:
dotnet add package Facet.Extensions
Then:
using Facet.Extensions;
// Single object
var dto = person.ToFacet<Person, PersonDto>();
// Lists
var dtos = people.SelectFacets<Person, PersonDto>();
// IQueryable deferred projection
var query = dbContext.People.SelectFacet<Person, PersonDto>();
And with EF Core:
var dtos = await dbContext.People.ToFacetsAsync<Person, PersonDto>();
var single = await dbContext.People.FirstFacetAsync<Person, PersonDto>();
Facet - Define less, project more.