v0.0.20#54
Conversation
…ckages Moves all mapping concerns out of BBT.Aether.Infrastructure into two new standalone packages: - BBT.Aether.Mapperly (default): compile-time source generator adapter with MapperBase<TSource,TDestination> and TwoWayMapperBase for bidirectional mapping, IMapperlyMapper / IReverseMapperlyMapper interfaces, and BeforeMap/AfterMap/BeforeReverseMap/AfterReverseMap lifecycle hooks. DI registration accepts List<Type> (assemblies derived internally). - BBT.Aether.AutoMapper (opt-in): runtime reflection adapter with AutoMapperOptions.LicenseKey for commercial license configuration. Emits NU1903 only for projects that explicitly opt in. BBT.Aether.Infrastructure no longer depends on AutoMapper or Riok.Mapperly. Projects using only Mapperly no longer receive the AutoMapper CVE build warning. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Change AddAetherAutoMapperMapper and AddAetherMapperlyMapper to accept IEnumerable<Type> for more flexible input. In AetherMapperlyServiceCollectionExtensions, unify interface registration and register implementations of IMapperlyMapper<,>, IReverseMapperlyMapper<,> and IObjectMapper<,> by checking generic interface definitions. In MapperlyAdapter, add a reverse fallback that looks up IReverseMapperlyMapper<TDestination, TSource> and invokes its BeforeReverseMap/ReverseMap/AfterReverseMap methods before throwing, enabling two-way mappers to be used when a direct forward mapper is not registered.
…pperly feat(mapper): extract Mapperly and AutoMapper into dedicated NuGet packages
Reviewer's GuideIntroduces Mapperly as the new default mapping implementation, splits mapping support into two dedicated NuGet packages (BBT.Aether.Mapperly and BBT.Aether.AutoMapper), wires them into DI and publishing, and overhauls the mapper documentation to describe the new architecture and usage patterns. Sequence diagram for MapperlyAdapter mapping resolutionsequenceDiagram
participant AppService
participant IObjectMapper as IObjectMapper
participant MapperlyAdapter as MapperlyAdapter
participant ServiceProvider as IServiceProvider
participant ForwardMapper as IMapperlyMapper_TSource_TDestination_
participant ReverseMapper as IReverseMapperlyMapper_TDestination_TSource_
AppService->>IObjectMapper: Map~TSource,TDestination~(source)
IObjectMapper->>MapperlyAdapter: Map~TSource,TDestination~(source)
alt Forward mapper registered
MapperlyAdapter->>ServiceProvider: GetService(IMapperlyMapper_TSource_TDestination_)
ServiceProvider-->>MapperlyAdapter: ForwardMapper
MapperlyAdapter->>ForwardMapper: BeforeMap(source)
MapperlyAdapter->>ForwardMapper: Map(source)
ForwardMapper-->>MapperlyAdapter: destination
MapperlyAdapter->>ForwardMapper: AfterMap(source, destination)
MapperlyAdapter-->>IObjectMapper: destination
IObjectMapper-->>AppService: destination
else No forward mapper, reverse mapper registered
MapperlyAdapter->>ServiceProvider: GetService(IMapperlyMapper_TSource_TDestination_)
ServiceProvider-->>MapperlyAdapter: null
MapperlyAdapter->>ServiceProvider: GetService(IReverseMapperlyMapper_TDestination_TSource_)
ServiceProvider-->>MapperlyAdapter: ReverseMapper
MapperlyAdapter->>ReverseMapper: BeforeReverseMap(source)
MapperlyAdapter->>ReverseMapper: ReverseMap(source)
ReverseMapper-->>MapperlyAdapter: destination
MapperlyAdapter->>ReverseMapper: AfterReverseMap(source, destination)
MapperlyAdapter-->>IObjectMapper: destination
IObjectMapper-->>AppService: destination
else No mapper registered
MapperlyAdapter-->>IObjectMapper: throw InvalidOperationException
IObjectMapper-->>AppService: exception
end
Class diagram for new Mapperly mapping architectureclassDiagram
direction LR
class IObjectMapper {
<<interface>>
+TDestination Map~TSource,TDestination~(TSource source)
+void Map~TSource,TDestination~(TSource source, TDestination destination)
}
class IObjectMapper_TSource_TDestination_ {
<<interface>>
+TDestination Map(TSource source)
+TDestination Map(TSource source, TDestination destination)
}
class IMapperlyMapper_TSource_TDestination_ {
<<interface>>
+TDestination Map(TSource source)
+TDestination Map(TSource source, TDestination destination)
+void BeforeMap(TSource source)
+void AfterMap(TSource source, TDestination destination)
}
class IReverseMapperlyMapper_TSource_TDestination_ {
<<interface>>
+TSource ReverseMap(TDestination destination)
+void ReverseMap(TDestination destination, TSource source)
+void BeforeReverseMap(TDestination destination)
+void AfterReverseMap(TDestination destination, TSource source)
}
class MapperBase_TSource_TDestination_ {
<<abstract>>
+TDestination Map(TSource source)
+TDestination Map(TSource source, TDestination destination)
+void BeforeMap(TSource source)
+void AfterMap(TSource source, TDestination destination)
}
class TwoWayMapperBase_TSource_TDestination_ {
<<abstract>>
+TSource ReverseMap(TDestination destination)
+void ReverseMap(TDestination destination, TSource source)
+void BeforeReverseMap(TDestination destination)
+void AfterReverseMap(TDestination destination, TSource source)
}
class MapperlyAdapter {
+MapperlyAdapter(IServiceProvider serviceProvider)
+TDestination Map~TSource,TDestination~(TSource source)
+void Map~TSource,TDestination~(TSource source, TDestination destination)
}
class AutoMapperOptions {
+string LicenseKey
}
class AutoMapperAdapter {
+AutoMapperAdapter(IMapper mapper)
+TDestination Map~TSource,TDestination~(TSource source)
+void Map~TSource,TDestination~(TSource source, TDestination destination)
}
class AutoMapperAdapter_TSource_TDestination_ {
+TDestination Map(TSource source)
+TDestination Map(TSource source, TDestination destination)
}
class AetherMapperlyServiceCollectionExtensions {
+static IServiceCollection AddAetherMapperlyMapper(IServiceCollection services, IEnumerable~Type~ mapperTypes)
}
class AetherAutoMapperServiceCollectionExtensions {
+static IServiceCollection AddAetherAutoMapperMapper(IServiceCollection services, IEnumerable~Type~ autoMapperTypes, Action~AutoMapperOptions~ configure)
}
class IServiceCollection {
}
class IServiceProvider {
}
class IMapper {
}
%% Inheritance and implementation
MapperBase_TSource_TDestination_ ..|> IMapperlyMapper_TSource_TDestination_
MapperBase_TSource_TDestination_ ..|> IObjectMapper_TSource_TDestination_
TwoWayMapperBase_TSource_TDestination_ --|> MapperBase_TSource_TDestination_
TwoWayMapperBase_TSource_TDestination_ ..|> IReverseMapperlyMapper_TSource_TDestination_
MapperlyAdapter ..|> IObjectMapper
AutoMapperAdapter ..|> IObjectMapper
AutoMapperAdapter_TSource_TDestination_ ..|> IObjectMapper_TSource_TDestination_
IReverseMapperlyMapper_TSource_TDestination_ ..|> IMapperlyMapper_TSource_TDestination_
%% DI relationships
MapperlyAdapter --> IServiceProvider
AutoMapperAdapter --> IMapper
AetherMapperlyServiceCollectionExtensions ..> MapperlyAdapter
AetherMapperlyServiceCollectionExtensions ..> IMapperlyMapper_TSource_TDestination_
AetherMapperlyServiceCollectionExtensions ..> IReverseMapperlyMapper_TSource_TDestination_
AetherMapperlyServiceCollectionExtensions ..> IObjectMapper_TSource_TDestination_
AetherAutoMapperServiceCollectionExtensions ..> AutoMapperAdapter
AetherAutoMapperServiceCollectionExtensions ..> AutoMapperAdapter_TSource_TDestination_
AetherAutoMapperServiceCollectionExtensions ..> AutoMapperOptions
AetherMapperlyServiceCollectionExtensions ..> IServiceCollection
AetherAutoMapperServiceCollectionExtensions ..> IServiceCollection
MapperlyAdapter ..> MapperBase_TSource_TDestination_
MapperlyAdapter ..> TwoWayMapperBase_TSource_TDestination_
File-Level Changes
Tips and commandsInteracting with Sourcery
Customizing Your ExperienceAccess your dashboard to:
Getting Help
|
|
Caution Review failedThe pull request is closed. Note
|
| Cohort / File(s) | Summary |
|---|---|
Mapperly Mapper Package framework/src/BBT.Aether.Mapperly/BBT.Aether.Mapperly.csproj, framework/src/BBT.Aether.Mapperly/BBT/Aether/Mapper/Mapperly/I*.cs, framework/src/BBT.Aether.Mapperly/BBT/Aether/Mapper/Mapperly/*Base.cs, framework/src/BBT.Aether.Mapperly/BBT/Aether/Mapper/Mapperly/MapperlyAdapter.cs, framework/src/BBT.Aether.Mapperly/Microsoft/Extensions/DependencyInjection/* |
New Mapperly-based mapper package with interfaces (IMapperlyMapper<,>, IReverseMapperlyMapper<,>), base classes (MapperBase<,>, TwoWayMapperBase<,>), service adapter (MapperlyAdapter), and DI extension for mapper registration and discovery. |
AutoMapper Package framework/src/BBT.Aether.AutoMapper/BBT.Aether.AutoMapper.csproj, framework/src/BBT.Aether.AutoMapper/BBT/Aether/Mapper/AutoMapper/AutoMapper*.cs, framework/src/BBT.Aether.AutoMapper/Microsoft/Extensions/DependencyInjection/* |
New AutoMapper-based mapper package with configuration options (AutoMapperOptions for license key), adapter, and DI extension supporting optional configuration and assembly scanning. |
Infrastructure Refactoring framework/src/BBT.Aether.Infrastructure/BBT.Aether.Infrastructure.csproj, framework/src/BBT.Aether.Infrastructure/Microsoft/Extensions/DependencyInjection/AetherMapperServiceCollectionExtensions.cs |
Removed AutoMapper package reference and extracted AddAetherAutoMapperMapper() method from Infrastructure to new AutoMapper package. |
Solution & Packaging Configuration framework/BBT.Aether.slnx, .github/workflows/publish-nuget.yml, Directory.Packages.props |
Added two mapper projects to solution; added projects to NuGet workflow PROJECTS list for automated packaging; added centralized Riok.Mapperly v4.1.1 package version. |
Documentation & Configuration CLAUDE.md, framework/docs/mapper/README.md, .gitignore |
Added Claude development guide; replaced mapper documentation with dual-package approach (Mapperly default, AutoMapper opt-in) including service registration, lifecycle hooks, comparison table, and integration examples; added tooling ignore patterns. |
Sequence Diagram
sequenceDiagram
participant Client
participant MapperlyAdapter
participant ServiceProvider
participant IMapperlyMapper as IMapperlyMapper<TSource, TDest>
participant IReverseMapper as IReverseMapperlyMapper<TDest, TSource>
Client->>MapperlyAdapter: Map(source)
activate MapperlyAdapter
MapperlyAdapter->>ServiceProvider: Resolve IMapperlyMapper<TSource, TDest>
alt Forward Mapper Found
ServiceProvider-->>MapperlyAdapter: mapper instance
MapperlyAdapter->>IMapperlyMapper: BeforeMap(source)
activate IMapperlyMapper
MapperlyAdapter->>IMapperlyMapper: Map(source)
MapperlyAdapter->>IMapperlyMapper: AfterMap(source, destination)
IMapperlyMapper-->>MapperlyAdapter: destination
deactivate IMapperlyMapper
MapperlyAdapter-->>Client: destination
else Forward Mapper Not Found
ServiceProvider-->>MapperlyAdapter: null
MapperlyAdapter->>ServiceProvider: Resolve IReverseMapperlyMapper<TDest, TSource>
alt Reverse Mapper Found
ServiceProvider-->>MapperlyAdapter: reverse mapper instance
MapperlyAdapter->>IReverseMapper: BeforeReverseMap(source)
activate IReverseMapper
MapperlyAdapter->>IReverseMapper: ReverseMap(source)
MapperlyAdapter->>IReverseMapper: AfterReverseMap(source, destination)
IReverseMapper-->>MapperlyAdapter: destination
deactivate IReverseMapper
MapperlyAdapter-->>Client: destination
else Neither Mapper Found
MapperlyAdapter-->>Client: InvalidOperationException
end
end
deactivate MapperlyAdapter
Estimated code review effort
🎯 3 (Moderate) | ⏱️ ~22 minutes
Possibly related PRs
- Add BBT.Aether.Aspects to NuGet publish workflow #18 — Modifies
.github/workflows/publish-nuget.ymlby adding mapper project csproj paths to the PROJECTS list for NuGet packaging - Upgrade to .NET 10 and update dependencies #42 — Updates
framework/BBT.Aether.slnx,Directory.Packages.props, and the publish-nuget workflow with overlapping solution and package configuration changes
Suggested labels
enhancement
Suggested reviewers
- ikarakayali
- middt
Poem
🐰 ✨ Two paths for mapping, both quite keen,
Mapperly swift and AutoMapper serene!
Adapters and base classes, all arranged with care,
From Infrastructure freed, new packages to share!
The adapter resolves forward and back with grace,
Lifecycle hooks dance at their destined place! 🎭
✨ Finishing Touches
📝 Generate docstrings
- Create stacked PR
- Commit on current branch
🧪 Generate unit tests (beta)
- Create PR with unit tests
- Commit unit tests in branch
master
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.
Comment @coderabbitai help to get the list of available commands and usage tips.
Tip
You can disable poems in the walkthrough.
Disable the reviews.poem setting to disable the poems in the walkthrough.
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request introduces Mapperly as the default object-to-object mapper within the Aether framework, while still providing AutoMapper as an opt-in alternative. The changes include new packages, modifications to the solution file, and updated documentation to guide developers in choosing and implementing the appropriate mapping strategy for their needs. Highlights
🧠 New Feature in Public Preview: You can now enable Memory to help Gemini Code Assist learn from your team's feedback. This makes future code reviews more consistent and personalized to your project's style. Click here to enable Memory in your admin console. Ignored Files
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In
MapperlyAdapter, the reverse-mapping fallback appears to have its generic type parameters swapped (usingIReverseMapperlyMapper<TDestination, TSource>and passingsourceintoBeforeReverseMap/ReverseMapwhich expect aTDestination), and the exception message type parameter order forTwoWayMapperBaseis inverted—both should be corrected to use a consistent<TSource, TDestination>direction and matching argument types. - The exception messages in the two
MapperlyAdapter.Mapoverloads are slightly inconsistent (one mentionsTwoWayMapperBase, the other does not) and could be unified to clearly describe both forward and reverse mapper expectations in the same way.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In `MapperlyAdapter`, the reverse-mapping fallback appears to have its generic type parameters swapped (using `IReverseMapperlyMapper<TDestination, TSource>` and passing `source` into `BeforeReverseMap`/`ReverseMap` which expect a `TDestination`), and the exception message type parameter order for `TwoWayMapperBase` is inverted—both should be corrected to use a consistent `<TSource, TDestination>` direction and matching argument types.
- The exception messages in the two `MapperlyAdapter.Map` overloads are slightly inconsistent (one mentions `TwoWayMapperBase`, the other does not) and could be unified to clearly describe both forward and reverse mapper expectations in the same way.
## Individual Comments
### Comment 1
<location path="framework/src/BBT.Aether.Mapperly/BBT/Aether/Mapper/Mapperly/MapperlyAdapter.cs" line_range="29-25" />
<code_context>
+ }
+
+ // Reverse fallback: look for a TwoWayMapper registered for the opposite direction.
+ var reverseMapper = serviceProvider.GetService<IReverseMapperlyMapper<TDestination, TSource>>();
+ if (reverseMapper is not null)
+ {
+ reverseMapper.BeforeReverseMap(source);
+ var destination = reverseMapper.ReverseMap(source);
+ reverseMapper.AfterReverseMap(source, destination);
+ return destination;
+ }
+
</code_context>
<issue_to_address>
**issue (bug_risk):** Reverse fallback type parameters and usage look conceptually inverted and may never match real TwoWayMapper registrations.
This fallback requests `IReverseMapperlyMapper<TDestination, TSource>` and then calls `ReverseMap(source)`, but your documented pattern is `IReverseMapperlyMapper<TSource, TDestination>` / `TwoWayMapperBase<TSource, TDestination>` with `TSource` as the forward direction. For an `Order -> OrderDto` mapper (`TwoWayMapperBase<Order, OrderDto>`), this code would instead search for `IReverseMapperlyMapper<OrderDto, Order>`, which likely doesn’t exist, and would call `ReverseMap` in the wrong direction. Consider either (a) looking up `IReverseMapperlyMapper<TSource, TDestination>` and using `Map` for `S -> D` and `ReverseMap` for `D -> S`, or (b) inverting the generic ordering in the docs/base types so the fallback can be configured consistently. As is, this path is very hard to wire correctly and probably never used.
</issue_to_address>
### Comment 2
<location path="framework/src/BBT.Aether.Mapperly/BBT/Aether/Mapper/Mapperly/MapperlyAdapter.cs" line_range="66-68" />
<code_context>
+ return;
+ }
+
+ throw new InvalidOperationException(
+ $"No mapper registered for {typeof(TSource).Name} → {typeof(TDestination).Name}. " +
+ $"Ensure a {nameof(MapperBase<TSource, TDestination>)} implementation is registered via AddAetherMapperlyMapper.");
+ }
+}
</code_context>
<issue_to_address>
**suggestion:** Exception messages are inconsistent about supported mapper types between the two overloads.
The parameterless overload lists both `MapperBase<TSource, TDestination>` and `TwoWayMapperBase<TDestination, TSource>` as valid, while this overload only mentions `MapperBase<TSource, TDestination>`. Please align the exception text between the two overloads to accurately reflect the set of supported mapper types for this path.
```suggestion
throw new InvalidOperationException(
$"No mapper registered for {typeof(TSource).Name} → {typeof(TDestination).Name}. " +
$"Ensure a {nameof(MapperBase<TSource, TDestination>)} or {nameof(TwoWayMapperBase<TDestination, TSource>)} implementation is registered via AddAetherMapperlyMapper.");
```
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
| mapper.BeforeMap(source); | ||
| var destination = mapper.Map(source); | ||
| mapper.AfterMap(source, destination); | ||
| return destination; |
There was a problem hiding this comment.
issue (bug_risk): Reverse fallback type parameters and usage look conceptually inverted and may never match real TwoWayMapper registrations.
This fallback requests IReverseMapperlyMapper<TDestination, TSource> and then calls ReverseMap(source), but your documented pattern is IReverseMapperlyMapper<TSource, TDestination> / TwoWayMapperBase<TSource, TDestination> with TSource as the forward direction. For an Order -> OrderDto mapper (TwoWayMapperBase<Order, OrderDto>), this code would instead search for IReverseMapperlyMapper<OrderDto, Order>, which likely doesn’t exist, and would call ReverseMap in the wrong direction. Consider either (a) looking up IReverseMapperlyMapper<TSource, TDestination> and using Map for S -> D and ReverseMap for D -> S, or (b) inverting the generic ordering in the docs/base types so the fallback can be configured consistently. As is, this path is very hard to wire correctly and probably never used.
| throw new InvalidOperationException( | ||
| $"No mapper registered for {typeof(TSource).Name} → {typeof(TDestination).Name}. " + | ||
| $"Ensure a {nameof(MapperBase<TSource, TDestination>)} implementation is registered via AddAetherMapperlyMapper."); |
There was a problem hiding this comment.
suggestion: Exception messages are inconsistent about supported mapper types between the two overloads.
The parameterless overload lists both MapperBase<TSource, TDestination> and TwoWayMapperBase<TDestination, TSource> as valid, while this overload only mentions MapperBase<TSource, TDestination>. Please align the exception text between the two overloads to accurately reflect the set of supported mapper types for this path.
| throw new InvalidOperationException( | |
| $"No mapper registered for {typeof(TSource).Name} → {typeof(TDestination).Name}. " + | |
| $"Ensure a {nameof(MapperBase<TSource, TDestination>)} implementation is registered via AddAetherMapperlyMapper."); | |
| throw new InvalidOperationException( | |
| $"No mapper registered for {typeof(TSource).Name} → {typeof(TDestination).Name}. " + | |
| $"Ensure a {nameof(MapperBase<TSource, TDestination>)} or {nameof(TwoWayMapperBase<TDestination, TSource>)} implementation is registered via AddAetherMapperlyMapper."); |
|
❌ The last analysis has failed. |
There was a problem hiding this comment.
Code Review
This pull request introduces a dual-mapper strategy, with Mapperly as the default and AutoMapper as an opt-in solution. The changes include new projects for both mappers, updated dependency injection wiring, and significantly improved documentation. My review focuses on the new Mapperly implementation, where I've suggested some improvements to parameter naming in the reverse mapping interfaces and base classes to enhance code clarity and maintainability.
| /// <summary>Maps <paramref name="destination"/> back to a new <typeparamref name="TSource"/> instance.</summary> | ||
| TSource ReverseMap(TDestination destination); | ||
|
|
||
| /// <summary>Maps <paramref name="destination"/> into the existing <paramref name="source"/> instance.</summary> | ||
| void ReverseMap(TDestination destination, TSource source); | ||
|
|
||
| /// <summary>Called before <see cref="ReverseMap(TDestination)"/> or <see cref="ReverseMap(TDestination,TSource)"/>.</summary> | ||
| void BeforeReverseMap(TDestination destination); | ||
|
|
||
| /// <summary>Called after <see cref="ReverseMap(TDestination)"/> or <see cref="ReverseMap(TDestination,TSource)"/>.</summary> | ||
| void AfterReverseMap(TDestination destination, TSource source); |
There was a problem hiding this comment.
The parameter names in the ReverseMap methods and their corresponding lifecycle hooks are a bit confusing. For instance, in ReverseMap(TDestination destination), the parameter destination is actually the source of the reverse mapping operation. To improve clarity and maintainability, I suggest renaming the parameters to reflect their roles within the reverse mapping context. This will make the code easier to understand, especially in the MapperlyAdapter implementation.
/// <summary>Maps <paramref name="source"/> back to a new <typeparamref name="TSource"/> instance.</summary>
TSource ReverseMap(TDestination source);
/// <summary>Maps <paramref name="source"/> into the existing <paramref name="destination"/> instance.</summary>
void ReverseMap(TDestination source, TSource destination);
/// <summary>Called before <see cref="ReverseMap(TDestination)"/> or <see cref="ReverseMap(TDestination,TSource)"/>.</summary>
void BeforeReverseMap(TDestination source);
/// <summary>Called after <see cref="ReverseMap(TDestination)"/> or <see cref="ReverseMap(TDestination,TSource)"/>.</summary>
void AfterReverseMap(TDestination source, TSource destination);| /// <inheritdoc /> | ||
| public abstract TSource ReverseMap(TDestination destination); | ||
|
|
||
| /// <inheritdoc /> | ||
| public abstract void ReverseMap(TDestination destination, TSource source); | ||
|
|
||
| /// <inheritdoc /> | ||
| public virtual void BeforeReverseMap(TDestination destination) { } | ||
|
|
||
| /// <inheritdoc /> | ||
| public virtual void AfterReverseMap(TDestination destination, TSource source) { } |
There was a problem hiding this comment.
Following the suggestion on the IReverseMapperlyMapper interface, the parameter names in this base class should also be updated for consistency and clarity. This will make implementing two-way mappers less error-prone.
/// <inheritdoc />
public abstract TSource ReverseMap(TDestination source);
/// <inheritdoc />
public abstract void ReverseMap(TDestination source, TSource destination);
/// <inheritdoc />
public virtual void BeforeReverseMap(TDestination source) { }
/// <inheritdoc />
public virtual void AfterReverseMap(TDestination source, TSource destination) { }
Summary by Sourcery
Introduce Mapperly as the default object-mapping implementation alongside an opt-in AutoMapper package, and wire both into the framework’s DI, documentation, and publishing pipeline.
New Features:
Enhancements:
Build:
Documentation:
Chores:
Summary by CodeRabbit
New Features
Refactor
Documentation