Repository navigation
Migration Migrate from OfX
FxMap is the successor of OfX. This guide maps the OfX attribute-based API to the FxMap FluentAPI, step by step.
Curious why the attribute design gave way to a fluent API? Read the story behind OfX and FxMap, or the original write-up on GitBook (in Vietnamese).
- Mapping is declared with
ProfileOf<T>andEntityConfigureOf<T>instead of attributes on your DTOs and models. - Distributed keys are
IDistributedKeytypes instead ofOfXAttributesubclasses. - Package names, namespaces and a few registration methods are renamed (tables below).
- The expression language:
Country.Name,Orders:sum(Total)and every other expression work unchanged. The one exception is the indexer and the filter, which 5.0 removed: see Upgrade to 5.0 if your expressions use them. - Transport registration:
AddNats,AddRabbitMq,AddKafka,AddAzureServiceBus,AddSqs,AddGrpcClients. - Configuration options:
ThrowIfException,SetRetryPolicy,SetRequestTimeOut,SetMaxNestingDepth,SetMaxConcurrentProcessing.
Remove every OfX package and add the matching FxMap package. All FxMap.* packages must use the same version.
| OfX package | FxMap package |
|---|---|
| OfX | FxMap |
| OfX.EntityFrameworkCore | FxMap.EntityFrameworkCore |
| OfX.MongoDb | FxMap.MongoDb |
| OfX.Grpc | FxMap.Grpc |
| OfX.Nats | FxMap.Nats |
| OfX.RabbitMq | FxMap.RabbitMq |
| OfX.Kafka | FxMap.Kafka |
| OfX.Azure.ServiceBus | FxMap.Azure.ServiceBus |
| OfX.Aws.Sqs | FxMap.Aws.Sqs |
| OfX.HotChocolate | FxMap.HotChocolate |
| OfX-Analyzers | FxMap.Analyzers |
// OfX
public sealed class UserOfAttribute(string propertyName) : OfXAttribute(propertyName);
// FxMap
public sealed class UserDistributedKey : IDistributedKey;On the service that owns the data, the [OfXConfigFor] attribute becomes an EntityConfigureOf<T> class. Your model stays free of mapping code.
// OfX
[OfXConfigFor<UserOfAttribute>(nameof(Id), nameof(Name))]
public sealed class User
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
// FxMap
public sealed class User
{
public string Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
public class UserConfig : EntityConfigureOf<User>
{
protected override void Configure()
{
Id(x => x.Id); // first argument of [OfXConfigFor]
DefaultProperty(x => x.Name); // second argument of [OfXConfigFor]
UseDistributedKey<UserDistributedKey>(); // the attribute type
}
}On the consuming service, each attribute on a DTO property becomes a .For(...) call in a profile. The expression string is copied as it is.
// OfX
public sealed class SomeDataResponse
{
public string UserId { get; set; }
[UserOf(nameof(UserId))]
public string UserName { get; set; }
[UserOf(nameof(UserId), Expression = "Email")]
public string UserEmail { get; set; }
}
// FxMap
public sealed class SomeDataResponse
{
public string UserId { get; set; }
public string UserName { get; set; }
public string UserEmail { get; set; }
}
public class SomeDataResponseProfile : ProfileOf<SomeDataResponse>
{
protected override void Configure() =>
UseDistributedKey<UserDistributedKey>()
.Of(x => x.UserId) // nameof(UserId)
.For(x => x.UserName) // no expression: DefaultProperty
.For(x => x.UserEmail, "Email"); // Expression = "Email"
}// OfX
builder.Services.AddOfX(cfg =>
{
cfg.AddAttributesContainNamespaces(typeof(UserOfAttribute).Assembly);
cfg.AddModelConfigurationsFromNamespaceContaining<User>();
cfg.AddNats(c => c.NatsOpts(o => o.Url = "nats://localhost:4222"));
}).AddOfXEFCore(o => o.AddDbContexts(typeof(AppDbContext)));
// FxMap
builder.Services.AddFxMap(cfg =>
{
cfg.AddEntitiesFromAssemblyContaining<User>(); // service that owns the data
cfg.AddProfilesFromAssemblyContaining<SomeDataResponse>(); // service that maps DTOs
cfg.AddNats(c => c.NatsOpts(o => o.Url = "nats://localhost:4222"));
}).AddEntityFrameworkCore(o => o.AddDbContexts(typeof(AppDbContext)));| OfX | FxMap |
|---|---|
| AddOfX(cfg => ...) | AddFxMap(cfg => ...) |
| cfg.AddAttributesContainNamespaces(...) | removed (there are no mapping attributes any more) |
| cfg.AddModelConfigurationsFromNamespaceContaining() | cfg.AddEntitiesFromAssemblyContaining() and cfg.AddProfilesFromAssemblyContaining() |
| .AddOfXEFCore(...) | .AddEntityFrameworkCore(...) |
| app.MapOfXGrpcService() | app.MapFxMapperGrpc() |
| IDistributedMapper.MapDataAsync(value, parameters, token) | IDistributedMapper.MapDataAsync(value, token) |
- Migrate the data-owning services and the consuming services together: a client on one library cannot talk to a server on the other.
- If you passed the
parametersargument toMapDataAsync, review that call: the FxMap method takes the value and a cancellation token only. - Turn on
cfg.ThrowIfException()in development while you migrate, so a missing key or entity config fails loudly instead of leaving a property empty. - Add the
FxMap.Analyzerspackage to catch misconfigured profiles and expressions at compile time.
Ask in the Discord server or open an issue on GitHub. Migration stories help us improve this guide.
Source: https://fxmapper.net/docs/migrate-from-ofx/ — the website is always the most up to date version.
Introduction
Getting Started
Configuration
- Service Registration
- Entities & Profiles
- Handlers
- Pipelines
- Strongly Typed IDs
- Error Handling
- Retry Policy
- Supervisor
- Telemetry
- Kestrel (HTTP/2)
- Analyzers
Expressions
- Overview
- Property Access
- Filters, Sorting & Paging
- Functions
- Aggregations
- Projections
- Coalesce & Ternary
- GroupBy
Data Providers
Transports
Integrations
Quality
AI Assistants
Migration
Release Notes