Facet.Extensions.EFCore.Mapping 5.0.0

There is a newer version of this package available.
See the version list below for details.
dotnet add package Facet.Extensions.EFCore.Mapping --version 5.0.0
                    
NuGet\Install-Package Facet.Extensions.EFCore.Mapping -Version 5.0.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="Facet.Extensions.EFCore.Mapping" Version="5.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Facet.Extensions.EFCore.Mapping" Version="5.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Facet.Extensions.EFCore.Mapping" />
                    
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 Facet.Extensions.EFCore.Mapping --version 5.0.0
                    
#r "nuget: Facet.Extensions.EFCore.Mapping, 5.0.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.
#:package Facet.Extensions.EFCore.Mapping@5.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Facet.Extensions.EFCore.Mapping&version=5.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Facet.Extensions.EFCore.Mapping&version=5.0.0
                    
Install as a Cake Tool

Facet.Extensions.EFCore.Mapping

Advanced custom async mapper support for Facet with EF Core queries. This package enables complex mappings that cannot be expressed as SQL projections, such as calling external services, complex type conversions (like Vector2), or async operations.

When to Use This Package

Use this package when you need to:

  • Call external services during mapping (e.g., geocoding, user lookup)
  • Perform complex type conversions that can't be translated to SQL
  • Execute async operations as part of the mapping process
  • Apply custom business logic that requires dependency injection

For simple projection-based mappings, use Facet.Extensions.EFCore instead.

Getting Started

1. Install packages

dotnet add package Facet.Extensions.EFCore.Mapping

This package automatically includes:

  • Facet.Extensions.EFCore - Core EF Core extensions
  • Facet.Mapping - Custom mapper interfaces

2. Import namespaces

using Facet.Extensions.EFCore.Mapping;  // For custom mapper methods
using Facet.Mapping;                     // For mapper interfaces

Usage Examples

Example 1: Simple Type Conversion (Vector2)

This addresses the GitHub issue #134 - converting separate X, Y properties into a Vector2 type.

// Domain entity
public class User
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public decimal X { get; set; }
    public decimal Y { get; set; }
}

// DTO with custom mapping
[Facet(typeof(User), exclude: ["X", "Y"])]
public partial class UserDto
{
    public Vector2 Position { get; set; }
}

// Static mapper (no DI needed)
public class UserMapper : IFacetMapConfigurationAsync<User, UserDto>
{
    public static async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default)
    {
        target.Position = new Vector2(source.X, source.Y);
        await Task.CompletedTask; // Or actual async work
    }
}

// Usage
var users = await dbContext.Users
    .Where(u => u.IsActive)
    .ToFacetsAsync<User, UserDto, UserMapper>();

var user = await dbContext.Users
    .Where(u => u.Id == userId)
    .FirstFacetAsync<User, UserDto, UserMapper>();

Example 2: Dependency Injection with External Services

// DTO
[Facet(typeof(User), exclude: ["LocationId"])]
public partial class UserDto
{
    public string Location { get; set; }
}

// Instance mapper with DI
public class UserMapper : IFacetMapConfigurationAsyncInstance<User, UserDto>
{
    private readonly ILocationService _locationService;

    public UserMapper(ILocationService locationService)
    {
        _locationService = locationService;
    }

    public async Task MapAsync(User source, UserDto target, CancellationToken cancellationToken = default)
    {
        // Call external service to resolve location name
        target.Location = await _locationService.GetLocationAsync(source.LocationId, cancellationToken);
    }
}

// Register in DI container
services.AddScoped<UserMapper>();

// Usage in controller
public class UsersController : ControllerBase
{
    private readonly ApplicationDbContext _context;
    private readonly UserMapper _userMapper;

    public UsersController(ApplicationDbContext context, UserMapper userMapper)
    {
        _context = context;
        _userMapper = userMapper;
    }

    [HttpGet]
    public async Task<ActionResult<List<UserDto>>> GetUsers()
    {
        return await _context.Users
            .Where(u => u.IsActive)
            .ToFacetsAsync<User, UserDto>(_userMapper);
    }
}

Example 3: Complex Business Logic

// DTO
[Facet(typeof(Order))]
public partial class OrderDto
{
    public string ShippingEstimate { get; set; }
    public decimal TotalWithTax { get; set; }
}

// Mapper with multiple services
public class OrderMapper : IFacetMapConfigurationAsyncInstance<Order, OrderDto>
{
    private readonly IShippingService _shippingService;
    private readonly ITaxCalculator _taxCalculator;

    public OrderMapper(IShippingService shippingService, ITaxCalculator taxCalculator)
    {
        _shippingService = shippingService;
        _taxCalculator = taxCalculator;
    }

    public async Task MapAsync(Order source, OrderDto target, CancellationToken cancellationToken = default)
    {
        // Calculate shipping estimate
        var shippingDays = await _shippingService.EstimateDeliveryAsync(
            source.ShippingAddress,
            cancellationToken);
        target.ShippingEstimate = $"{shippingDays} business days";

        // Calculate tax
        var tax = await _taxCalculator.CalculateTaxAsync(
            source.Total,
            source.ShippingAddress.State,
            cancellationToken);
        target.TotalWithTax = source.Total + tax;
    }
}

API Reference

Method Description
ToFacetsAsync<TSource, TTarget>(mapper) Project query to list with instance mapper (DI support)
ToFacetsAsync<TSource, TTarget, TAsyncMapper>() Project query to list with static async mapper
FirstFacetAsync<TSource, TTarget>(mapper) Get first with instance mapper (DI support)
FirstFacetAsync<TSource, TTarget, TAsyncMapper>() Get first with static async mapper
SingleFacetAsync<TSource, TTarget>(mapper) Get single with instance mapper (DI support)
SingleFacetAsync<TSource, TTarget, TAsyncMapper>() Get single with static async mapper

How It Works

  1. Query Execution: The EF Core query is executed first, materializing entities from the database
  2. Auto-Mapping: All matching properties are automatically mapped (like the base Facet behavior)
  3. Custom Mapping: Your custom mapper is called to handle additional logic

Important: These methods execute the SQL query first, then apply custom mapping. This means:

  • ✅ You can use async operations, external services, and complex logic
  • ⚠️ The query is materialized before custom mapping (can't be translated to SQL)
  • 💡 Use standard ToFacetsAsync from Facet.Extensions.EFCore if you only need SQL projections

Mapper Interfaces

Static Mapper (No DI)

public class MyMapper : IFacetMapConfigurationAsync<TSource, TTarget>
{
    public static async Task MapAsync(TSource source, TTarget target, CancellationToken cancellationToken = default)
    {
        // Custom mapping logic
    }
}

Instance Mapper (With DI)

public class MyMapper : IFacetMapConfigurationAsyncInstance<TSource, TTarget>
{
    private readonly IMyService _service;

    public MyMapper(IMyService service)
    {
        _service = service;
    }

    public async Task MapAsync(TSource source, TTarget target, CancellationToken cancellationToken = default)
    {
        // Custom mapping logic with injected services
    }
}

Requirements

  • Facet v1.6.0+
  • Facet.Extensions.EFCore v1.0.0+
  • Facet.Mapping v1.0.0+
  • Entity Framework Core 6+
  • .NET 6+
Product Compatible and additional computed target framework versions.
.NET net8.0 is compatible.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in 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
5.2.0 0 12/23/2025
5.2.0-alpha 264 12/18/2025
5.1.12 250 12/14/2025
5.1.11 454 12/9/2025
5.1.10 232 12/5/2025
5.1.9 657 12/3/2025
5.1.8 666 12/1/2025
5.1.7 483 12/1/2025
5.1.6 109 11/28/2025
5.1.5 107 11/28/2025
5.1.4 126 11/28/2025
5.1.3 139 11/28/2025
5.1.2 171 11/27/2025
5.1.1 163 11/27/2025
5.1.0 162 11/27/2025
5.0.4 176 11/26/2025
5.0.3 167 11/26/2025
5.0.2 179 11/26/2025
5.0.1 181 11/25/2025
5.0.0 175 11/24/2025
5.0.0-alpha5 180 11/24/2025
5.0.0-alpha4 150 11/23/2025
5.0.0-alpha3 166 11/22/2025
5.0.0-alpha2 169 11/22/2025
5.0.0-alpha 257 11/21/2025
4.4.3 301 11/21/2025
4.4.2 329 11/21/2025
4.4.1 384 11/20/2025
4.4.1-alpha4 266 11/16/2025
4.4.1-alpha3 260 11/16/2025
4.4.1-alpha2 268 11/16/2025
4.4.1-alpha 268 11/16/2025
4.4.0 208 11/15/2025
4.4.0-alpha 207 11/14/2025
4.3.3.1 207 11/14/2025
4.3.3 209 11/14/2025
4.3.2.1 209 11/14/2025
4.3.2 278 11/13/2025
4.3.1 271 11/12/2025
4.3.0 119 11/8/2025
4.3.0-alpha 130 11/7/2025
3.4.0 112 11/8/2025
3.3.0 140 11/7/2025
1.0.0 174 11/27/2025