Facet.Extensions.EFCore.Mapping 5.1.11

dotnet add package Facet.Extensions.EFCore.Mapping --version 5.1.11
                    
NuGet\Install-Package Facet.Extensions.EFCore.Mapping -Version 5.1.11
                    
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.1.11" />
                    
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.1.11" />
                    
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.1.11
                    
#r "nuget: Facet.Extensions.EFCore.Mapping, 5.1.11"
                    
#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.1.11
                    
#: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.1.11
                    
Install as a Cake Addin
#tool nuget:?package=Facet.Extensions.EFCore.Mapping&version=5.1.11
                    
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.1.11 207 12/9/2025
5.1.10 227 12/5/2025
5.1.9 651 12/3/2025
5.1.8 658 12/1/2025
5.1.7 475 12/1/2025
5.1.6 105 11/28/2025
5.1.5 103 11/28/2025
5.1.4 120 11/28/2025
5.1.3 133 11/28/2025
5.1.2 168 11/27/2025
5.1.1 159 11/27/2025
5.1.0 157 11/27/2025
5.0.4 170 11/26/2025
5.0.3 159 11/26/2025
5.0.2 173 11/26/2025
5.0.1 175 11/25/2025
5.0.0 170 11/24/2025
5.0.0-alpha5 175 11/24/2025
5.0.0-alpha4 147 11/23/2025
5.0.0-alpha3 161 11/22/2025
5.0.0-alpha2 165 11/22/2025
5.0.0-alpha 251 11/21/2025
4.4.3 294 11/21/2025
4.4.2 321 11/21/2025
4.4.1 379 11/20/2025
4.4.1-alpha4 261 11/16/2025
4.4.1-alpha3 255 11/16/2025
4.4.1-alpha2 263 11/16/2025
4.4.1-alpha 262 11/16/2025
4.4.0 201 11/15/2025
4.4.0-alpha 203 11/14/2025
4.3.3.1 200 11/14/2025
4.3.3 204 11/14/2025
4.3.2.1 203 11/14/2025
4.3.2 275 11/13/2025
4.3.1 268 11/12/2025
4.3.0 113 11/8/2025
4.3.0-alpha 128 11/7/2025
3.4.0 106 11/8/2025
3.3.0 135 11/7/2025
1.0.0 170 11/27/2025