42Entwickler.EntityMask.Generator 0.1.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package 42Entwickler.EntityMask.Generator --version 0.1.0
                    
NuGet\Install-Package 42Entwickler.EntityMask.Generator -Version 0.1.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="42Entwickler.EntityMask.Generator" Version="0.1.0">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="42Entwickler.EntityMask.Generator" Version="0.1.0" />
                    
Directory.Packages.props
<PackageReference Include="42Entwickler.EntityMask.Generator">
  <PrivateAssets>all</PrivateAssets>
  <IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add 42Entwickler.EntityMask.Generator --version 0.1.0
                    
#r "nuget: 42Entwickler.EntityMask.Generator, 0.1.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#addin nuget:?package=42Entwickler.EntityMask.Generator&version=0.1.0
                    
Install 42Entwickler.EntityMask.Generator as a Cake Addin
#tool nuget:?package=42Entwickler.EntityMask.Generator&version=0.1.0
                    
Install 42Entwickler.EntityMask.Generator as a Cake Tool

EntityMask

EntityMask is a powerful, lightweight framework for creating strongly typed view projections of your domain entities through code generation. It allows you to define "masks" that expose only selected properties of your entities, transform values, and support deep object mapping without runtime reflection.

Packages

Package Description NuGet
42Entwickler.EntityMask Core library with attributes and runtime components NuGet
42Entwickler.EntityMask.Generator Source generator that creates mask classes at compile time NuGet

Getting Started

Installation

# Install the core library
dotnet add package 42Entwickler.EntityMask

# Install the source generator
dotnet add package 42Entwickler.EntityMask.Generator

Basic Usage

  1. Define your entity model
  2. Add mask attributes to specify how properties should be exposed
  3. Use the generated mask classes to create projections
using _42Entwickler.EntityMask;

// 1. Define entity with mask attributes
[EntityMask("api")]  // Create an API mask
public class Customer
{
    public int Id { get; set; }
    public string Name { get; set; }
    
    // This property will be hidden in the API mask
    [Mask("api")]
    public string InternalNotes { get; set; }
    
    // This collection will be deeply mapped if Customer is accessed through 
    // a mask with deep mapping enabled
    public List<Order> Orders { get; set; }
}

// 2. Use the generated mask
public class CustomerService
{
    public CustomerApiMask GetCustomerForApi(int id)
    {
        Customer customer = _repository.GetCustomer(id);        
        // Return by implicit conversion overload
        return customer;

        // Or by direct conversion to mask using extension method
        // return customer.ToApiMask();
        
        // Or using the generic factory
        // return EntityMask.CreateMask<Customer, CustomerApiMask>(customer, "api");
    }
}

Features

Multiple Mask Types

Define multiple different mask types for each entity to support different views:

[EntityMask("api")]      // Public API mask
[EntityMask("admin")]    // Admin view mask
[EntityMask("internal")] // Internal system mask
public class Product
{
    public int Id { get; set; }
    
    public string Name { get; set; }
    
    [Mask("api")]        // Hide in API mask
    public decimal Cost { get; set; }
    
    [Mask("api", "admin")] // Hide in both API and admin masks
    public string SupplierNotes { get; set; }
}

Value Transformers

Transform property values when exposed through a mask:

public class User
{
    // Transform phone number in the API mask
    [TransformInMask("FormatPhoneNumber", "api")]
    public string PhoneNumber { get; set; }
    
    // Static transformer method - must be public and static
    public static string FormatPhoneNumber(string phone)
    {
        if (string.IsNullOrEmpty(phone) || phone.Length != 10)
            return phone;
            
        return $"{phone.Substring(0, 3)}-{phone.Substring(3, 3)}-{phone.Substring(6)}";
    }
}

Value Converters

Perform bidirectional conversion of property values with custom converters:

public class Customer
{
    // Use a custom converter for date formatting
    [ConvertInMask(typeof(DateTimeToStringConverter))]
    public DateTime BirthDate { get; set; }
}

// Converter must implement IValueConverter<TEntity, TMask>
public class DateTimeToStringConverter : IValueConverter<DateTime, string>
{
    public string ConvertToMask(DateTime value)
    {
        return value.ToString("yyyy-MM-dd");
    }
    
    public DateTime ConvertToEntity(string value)
    {
        return DateTime.Parse(value);
    }
}

Deep Mapping

Enable deep mapping to automatically convert nested objects and collections:

// Enable deep mapping for nested objects
[EntityMask("api", EnableDeepMapping = true)]
public class Order
{
    public int Id { get; set; }
    public DateTime OrderDate { get; set; }
    
    // This will be automatically mapped to CustomerApiMask if Customer has an API mask
    public Customer Customer { get; set; }
    
    // Collection properties are also automatically mapped
    public List<OrderItem> Items { get; set; }
}

Custom Mask Class Names

Specify custom class names for your mask types:

[EntityMask("api", ClassName = "ProductDto")]
public class Product
{
    public int Id { get; set; }
    public string Name { get; set; }
}

// Usage
var productDto = product.ToProductDto();  // Instead of .ToApiMask()

Advanced Features

Collection Handling

EntityMask efficiently handles collections through proxy collections that create mask instances on-demand:

// Get a collection of Customer entities
IEnumerable<Customer> customers = _repository.GetAllCustomers();

// Convert to mask collection without eagerly creating all masks
IEnumerable<CustomerApiMask> customerMasks = 
    EntityMask.CreateMaskes<Customer>(customers, "api");
    
// Masks are created only when accessed
foreach (var mask in customerMasks)
{
    // CustomerApiMask created here when accessed
    Console.WriteLine(mask.Name);
}

Bidirectional Mapping

Mask classes support bidirectional mapping, allowing you to modify the underlying entity through the mask:

// Get entity with mask
var customerMask = customer.ToApiMask();

// Modify through mask
customerMask.Name = "New Name";

// Changes are applied to the original entity
Console.WriteLine(customer.Name);  // Outputs: New Name

// Implicit conversions
Customer originalEntity = customerMask;  // Get the original entity

Custom Collection Converters

Implement custom collection converters for specialized collection mapping:

public class CustomCollectionConverter<T> : ICollectionConverter<T, CustomMask<T>>
{
    public IEnumerable<CustomMask<T>> ConvertToMask(IEnumerable<T> collection)
    {
        // Custom conversion logic
        return collection.Select(item => new CustomMask<T>(item));
    }
    
    public IEnumerable<T> ConvertToEntity(IEnumerable<CustomMask<T>> collection)
    {
        // Custom conversion back
        return collection.Select(mask => mask.GetEntity());
    }
}

Performance Considerations

  • EntityMask uses compile-time code generation for optimal performance
  • No runtime reflection is used when accessing properties
  • Collection proxies defer mask creation until needed
  • All conversion happens through strongly typed code

Source Generator Analyzer

The EntityMask source generator includes analyzers that help you avoid common mistakes:

Transformer Method Validation (EM001)

Ensures transformer methods are correctly defined:

// ERROR: Missing or invalid transformer method
[TransformInMask("FormatAddress")]  // Method doesn't exist or has wrong signature
public string Address { get; set; }

// FIXED:
public static string FormatAddress(string address)
{
    // Implementation
}

Converter Type Validation (EM002)

Ensures converter types implement the correct interface:

// ERROR: Invalid converter type
[ConvertInMask(typeof(InvalidConverter))]  // Doesn't implement IValueConverter<DateTime, string>
public DateTime BirthDate { get; set; }

// FIXED:
public class DateConverter : IValueConverter<DateTime, string>
{
    public string ConvertToMask(DateTime value) => value.ToString("yyyy-MM-dd");
    public DateTime ConvertToEntity(string value) => DateTime.Parse(value);
}

Deep Mapping Warning (EM003)

Warns when deep mapping is enabled but not needed:

// WARNING: No collection properties to deeply map
[EntityMask("api", EnableDeepMapping = true)]
public class SimpleEntity  // No collection properties
{
    public string Name { get; set; }
}

Examples

Complete example with multiple features:

[EntityMask("api")]
[EntityMask("admin", EnableDeepMapping = true)]
public class Customer
{
    public int Id { get; set; }
    
    public string Name { get; set; }
    
    [ConvertInMask(typeof(DateTimeToStringConverter))]
    public DateTime BirthDate { get; set; }
    
    [TransformInMask("FormatPhoneNumber", "api")]
    public string PhoneNumber { get; set; }
    
    [Mask("api")]
    public decimal CreditLimit { get; set; }
    
    public List<Order> Orders { get; set; }
    
    public static string FormatPhoneNumber(string phone)
    {
        if (string.IsNullOrEmpty(phone) || phone.Length != 10)
            return phone;
            
        return $"{phone.Substring(0, 3)}-{phone.Substring(3, 3)}-{phone.Substring(6)}";
    }
}

public class DateTimeToStringConverter : IValueConverter<DateTime, string>
{
    public string ConvertToMask(DateTime value) => value.ToString("yyyy-MM-dd");
    public DateTime ConvertToEntity(string value) => DateTime.Parse(value);
}

// Usage
public class CustomerService
{
    public IEnumerable<CustomerApiMask> GetCustomersForApi()
    {
        var customers = _repository.GetCustomers();
        return customers.Select(c => c.ToApiMask());
    }
    
    public CustomerAdminMask GetCustomerForAdmin(int id)
    {
        var customer = _repository.GetCustomer(id);
        
        // Deep mapping - Order objects are converted to OrderAdminMask
        return customer.ToAdminMask();
    }
}

License

This project is licensed under the MIT License - see the LICENSE file for details.

Donations

Welcome - with or without invoice as you like just mail me at 42entwickler -- a-t -- gmail.com

There are no supported framework assets in this package.

Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last updated
0.1.0.6 38 5/19/2025
0.1.0.5 41 5/19/2025
0.1.0.4 38 5/18/2025
0.1.0.3 38 5/18/2025
0.1.0.2 43 5/18/2025
0.1.0.1 43 5/17/2025
0.1.0 40 5/17/2025