Skip to content
quyvu01Public

About

Distributed data mapping for .NET — declare where a property comes from, and FxMap fetches it from the service that owns the data, over the transport you already run.

Topics

Resources

Code of conduct

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

FxMap

FxMap

Distributed data mapping for .NET — declare where a property comes from, and FxMap fetches it from the service that owns the data, over the transport you already run.

NuGet Downloads Build License .NET

Documentation · Getting Started · Expression Language · Discord


In a microservice system a response often carries only keys: an order has a UserId, a ProvinceId, a ProductId. Showing it means collecting UserName, ProvinceName, ProductName from the services that own them, and writing that glue again for every endpoint. FxMap turns the glue into a declaration:

public class OrderResponse
{
    public string UserId { get; set; }
    public string UserName { get; set; }     // filled by FxMap
    public string UserEmail { get; set; }    // filled by FxMap
}

public class OrderResponseProfile : ProfileOf<OrderResponse>
{
    protected override void Configure() =>
        UseDistributedKey<UserDistributedKey>()
            .Of(x => x.UserId)
            .For(x => x.UserName)
            .For(x => x.UserEmail, "Email");
}

await distributedMapper.MapDataAsync(response);   // one batched request per key, however many objects

The service that owns the data registers an entity configuration and a data provider. FxMap batches the ids, sends them over a transport (or answers locally when the owner is the same service), and writes the results back. No client code per field, and no coupling between the two services beyond a key name.

Features

  • FluentAPI mapping: declare the mapping with ProfileOf<T> and EntityConfigureOf<T>; no attributes on your DTOs, and property access is compiled once per type.
  • Expression language: a SQL-like DSL for navigation, filters, aggregations, projections, indexers and conditions (Orders(Status = 'Done'):sum(Total)).
  • Batched and chained: one request per distributed key and dependency level, with distinct ids, however many objects are mapped; chains such as UserId → ProvinceId → CountryId resolve level by level.
  • Data providers: Entity Framework Core and MongoDB.
  • Transports: gRPC, NATS, RabbitMQ, Kafka, Azure Service Bus and Amazon SQS, with retries and supervision.
  • GraphQL integration with HotChocolate.
  • Compile-time checks: Roslyn analyzers for misconfigured profiles and entities.
  • Observable: activities for OpenTelemetry tracing on mapping and data access.
  • Targets .NET 8, 9 and 10.

Warning

All FxMap.* packages need to have the same version.

Quick Start

dotnet add package FxMap
// 1. Configure FxMap
builder.Services.AddFxMap(cfg =>
{
    cfg.AddEntitiesFromAssemblyContaining<SomeEntityAssemblyMarker>();
    cfg.AddProfilesFromAssemblyContaining<SomeProfileAssemblyMarker>();
});

// 2. Define a distributed key
public sealed class UserDistributedKey : IDistributedKey;

// 3. Configure the entity with FluentAPI
public class UserConfig : EntityConfigureOf<User>
{
    protected override void Configure()
    {
        Id(x => x.Id);
        DefaultProperty(x => x.Name);
        UseDistributedKey<UserDistributedKey>(); // Or you want to absolute lose coupling, you can use: UseDistributedKey("UserDistributedKey")
        ExposedName(x => x.Email, "UserEmail");
    }
}

// 4. Define a profile for your DTO
public class UserResponseProfile : ProfileOf<UserResponse>
{
    protected override void Configure()
    {
        UseDistributedKey<UserDistributedKey>() // Or you want to absolute lose coupling, you can use: UseDistributedKey("UserDistributedKey")
            .Of(x => x.UserId)
            .For(x => x.UserName)
            .For(x => x.UserEmail, "Email");
    }
}

Expression Examples

public class UserResponseProfile : ProfileOf<UserResponse>
{
    protected override void Configure()
    {
        UseDistributedKey<UserDistributedKey>()
            .Of(x => x.UserId)
            // Simple property access
            .For(x => x.UserEmail, "Email")
            // Navigation properties
            .For(x => x.CountryName, "Country.Name")
            // Filtering
            .For(x => x.CompletedOrders, "Orders(Status = 'Done')")
            // Aggregation
            .For(x => x.TotalSpent, "Orders:sum(Total)")
            // Projection
            .For(x => x.UserDetails, "{Id, Name, Address.City as CityName}")
            // GroupBy
            .For(x => x.OrdersByStatus, "Orders:groupBy(Status).{Status, :count as Count}");
    }
}

For complete expression syntax including filters, indexers, functions, aggregations, boolean functions, coalesce, ternary operators, and more, visit Expression Documentation.

Packages

Package Description .NET Documentation
Core
FxMap FxMap core: profiles, entity configs, the mapper and the expression language 8.0, 9.0, 10.0 fxmapper.net
Data providers
FxMap.EntityFrameworkCore Answers requests from an Entity Framework Core DbContext 8.0, 9.0, 10.0 README
FxMap.MongoDb Answers requests from MongoDB collections 8.0, 9.0, 10.0 README
Integrations
FxMap.HotChocolate Fills GraphQL response types through HotChocolate 8.0, 9.0, 10.0 README
Transports
FxMap.Grpc gRPC transport 8.0, 9.0, 10.0 README
FxMap.Nats NATS transport 8.0, 9.0, 10.0 README
FxMap.RabbitMq RabbitMQ transport 8.0, 9.0, 10.0 README
FxMap.Kafka Apache Kafka transport 8.0, 9.0, 10.0 README
FxMap.Azure.ServiceBus Azure Service Bus transport (Standard / Premium tiers) 8.0, 9.0, 10.0 README
FxMap.Aws.Sqs Amazon SQS transport 8.0, 9.0, 10.0 README
Tooling
FxMap.Analyzers Roslyn analyzer that validates expression strings at compile time 8.0, 9.0, 10.0 README

Performance

FxMap is built to enrich data that lives in other services, so most of a real call is spent waiting on the network. This benchmark removes the network to measure only the mapper itself: FxMap enriches DTOs from an EF Core InMemory database in the same process (no transport), compared with AutoMapper 14 ProjectTo doing a single projected query.

Scenario: each order has a customer (customer → province → country) and 3-5 items (item → product → category). Both sides produce the same OrderDto graph (the benchmark checks that the results are identical).

  • AutoMapper: db.Orders.ProjectTo<OrderDto>(config), one query with joins.
  • FxMap: load the DTOs with only their scalar values and keys, then IDistributedMapper.MapDataAsync(dtos) resolves customer, province, country, product and category through ProfileOf<T> and the EF Core data provider.
Orders AutoMapper ProjectTo FxMap (query + enrich) FxMap time vs AutoMapper FxMap memory vs AutoMapper
10 164 µs / 276 KB 277 µs / 382 KB 1.69× 1.38×
100 2.07 ms / 2.2 MB 1.81 ms / 1.1 MB 0.88× 0.50×
1,000 96.4 ms / 21.4 MB 93.6 ms / 7.0 MB 0.97× 0.33×

BenchmarkDotNet 0.15, 3 launches, .NET 10, Apple M1 Pro. Lower is better.

How to read it:

  • Past a few dozen objects FxMap is on par with or faster than a single joined ProjectTo and allocates a half to a third of the memory, because it fetches each distinct key once instead of joining every row.
  • With a handful of objects both take well under a millisecond; the fixed cost of one scoped query per distributed key and level shows up (about 0.1 ms here) and FxMap is slower in relative terms.
  • At 1,000 orders the in-memory query itself is most of the time (about 84 ms of the 94 ms), so the gap between the two is small there.
  • This is a local, single-process comparison on an in-memory provider. With a real database or transport, the cost of the round trips dominates and FxMap's batching by key is what matters; these numbers say nothing about network latency.

To reproduce:

cd FxMap/test/FxMap.Benchmark
dotnet run -c Release -- --filter '*ProjectionBenchmark*' --launchCount 3

Documentation

Visit fxmapper.net for:

Community

Contributing

Contributions are welcome! See CONTRIBUTING.md or visit our GitHub repository to:

  • Report issues
  • Submit pull requests
  • Request features

License

This project is licensed under the Apache-2.0 license.


About

Distributed data mapping for .NET — declare where a property comes from, and FxMap fetches it from the service that owns the data, over the transport you already run.

Topics

Resources

Code of conduct

Contributing

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages