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
<PackageReference Include="Facet.Extensions.EFCore.Mapping" Version="5.1.11" />
<PackageVersion Include="Facet.Extensions.EFCore.Mapping" Version="5.1.11" />
<PackageReference Include="Facet.Extensions.EFCore.Mapping" />
paket add Facet.Extensions.EFCore.Mapping --version 5.1.11
#r "nuget: Facet.Extensions.EFCore.Mapping, 5.1.11"
#:package Facet.Extensions.EFCore.Mapping@5.1.11
#addin nuget:?package=Facet.Extensions.EFCore.Mapping&version=5.1.11
#tool nuget:?package=Facet.Extensions.EFCore.Mapping&version=5.1.11
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 extensionsFacet.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
- Query Execution: The EF Core query is executed first, materializing entities from the database
- Auto-Mapping: All matching properties are automatically mapped (like the base Facet behavior)
- 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
ToFacetsAsyncfromFacet.Extensions.EFCoreif 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+
Related Packages
- Facet - Core source generator
- Facet.Extensions - Provider-agnostic extensions
- Facet.Extensions.EFCore - Basic EF Core async extensions
- Facet.Mapping - Custom mapping interfaces
| Product | Versions 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. |
-
net10.0
- Facet.Extensions.EFCore (>= 5.1.11)
- Facet.Mapping (>= 5.1.11)
- Microsoft.EntityFrameworkCore (>= 10.0.0)
-
net8.0
- Facet.Extensions.EFCore (>= 5.1.11)
- Facet.Mapping (>= 5.1.11)
- Microsoft.EntityFrameworkCore (>= 8.0.11)
- System.Collections.Immutable (>= 9.0.10)
-
net9.0
- Facet.Extensions.EFCore (>= 5.1.11)
- Facet.Mapping (>= 5.1.11)
- Microsoft.EntityFrameworkCore (>= 9.0.10)
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 |