Expression tree transformation and mapping utilities for Facet DTOs. Transform predicates, selectors, and other expressions between source entities and their Facet projections.
$ dotnet add package Facet.Mapping.ExpressionsExpression tree transformation and mapping utilities for Facet DTOs. Transform predicates, selectors, and other expressions between source entities and their Facet projections.
dotnet add package Facet.Mapping.Expressions
Transform business logic from entities to DTOs:
using Facet.Mapping.Expressions;
// Original predicate for entity
Expression<Func<User, bool>> entityFilter = u => u.IsActive && u.Age > 18;
// Transform to work with DTO
Expression<Func<UserDto, bool>> dtoFilter = entityFilter.MapToFacet<UserDto>();
// Use with LINQ queries
var results = dtoCollection.Where(dtoFilter.Compile()).ToList();
Transform sorting and selection logic:
// Original selector for entity
Expression<Func<User, string>> entitySelector = u => u.LastName;
// Transform to work with DTO
Expression<Func<UserDto, string>> dtoSelector = entitySelector.MapToFacet<UserDto, string>();
// Use for sorting
var sorted = dtoCollection.OrderBy(dtoSelector.Compile()).ToList();
Combine multiple conditions:
var isAdult = (Expression<Func<User, bool>>)(u => u.Age >= 18);
var isActive = (Expression<Func<User, bool>>)(u => u.IsActive);
var isVip = (Expression<Func<User, bool>>)(u => u.IsVip);
// Combine with AND
var adultAndActive = FacetExpressionExtensions.CombineWithAnd(isAdult, isActive);
// Combine with OR
var vipOrAdult = FacetExpressionExtensions.CombineWithOr(isVip, isAdult);
// Negate a condition
var inactive = isActive.Negate();
// Transform composed expressions to DTOs
var dtoFilter = adultAndActive.MapToFacet<UserDto>();
Handle complex expressions with anonymous objects, method calls, etc.:
// Complex expression with method calls and projections
Expression<Func<User, object>> complexExpr = u => new {
FullName = u.FirstName + " " + u.LastName,
IsEligible = u.Age > 21 && u.Email.Contains("@company.com"),
DisplayAge = u.Age.ToString()
};
// Transform to DTO context
var dtoExpr = complexExpr.MapToFacetGeneric<UserDto>();
Common pattern for reusable business logic:
public class UserRepository
{
private readonly Expression<Func<User, bool>> _activeUsersFilter =
u => u.IsActive && !u.IsDeleted;
public IQueryable<User> GetActiveUsers(IQueryable<User> query)
{
return query.Where(_activeUsersFilter);
}
public IEnumerable<UserDto> GetActiveUserDtos(IEnumerable<UserDto> dtos)
{
var dtoFilter = _activeUsersFilter.MapToFacet<UserDto>();
return dtos.Where(dtoFilter.Compile());
}
}
Build complex queries dynamically:
public static class UserFilters
{
public static Expression<Func<User, bool>> ByAgeRange(int minAge, int maxAge) =>
u => u.Age >= minAge && u.Age <= maxAge;
public static Expression<Func<User, bool>> ByStatus(bool isActive) =>
u => u.IsActive == isActive;
public static Expression<Func<User, bool>> ByEmailDomain(string domain) =>
u => u.Email.EndsWith("@" + domain);
}
// Combine filters dynamically
var filters = new List<Expression<Func<User, bool>>>();
if (ageFilter.HasValue)
filters.Add(UserFilters.ByAgeRange(ageFilter.Value.Min, ageFilter.Value.Max));
if (activeOnly)
filters.Add(UserFilters.ByStatus(true));
if (!string.IsNullOrEmpty(emailDomain))
filters.Add(UserFilters.ByEmailDomain(emailDomain));
// Combine all filters
var combinedFilter = FacetExpressionExtensions.CombineWithAnd(filters.ToArray());
// Apply to both entities and DTOs
var entityResults = entityQuery.Where(combinedFilter);
var dtoFilter = combinedFilter.MapToFacet<UserDto>();
var dtoResults = dtoCollection.Where(dtoFilter.Compile());
The library uses expression tree visitors to transform expressions from source types to target (Facet) types:
==, !=, >, <, etc.), logical operations (&&, ||)!), conversionsu.Name, u.Age)This library works seamlessly with all Facet-generated types:
[Facet] attributerecord keyword)struct keyword)Facet.Mapping)