Full nameof feature behaves inconsistently and lacks clear documentation
Dear Mapperly team,
I’ve encountered some confusing behavior with the full nameof feature when using the [MapProperty] attribute. According to the documentation, it should be possible to write something like:
[MapProperty(nameof(Car.Make.Id), nameof(CarDto.MakeId))]
Even though nameof(Car.Make.Id) is not valid in plain C# (it would just return "Id"), Mapperly seems to support this extended syntax.
However, the documentation mentions that in some cases, the @ symbol must be used, like this:
[MapProperty(nameof(@Car.Make.Id), nameof(CarDto.MakeId))]
Unfortunately, it’s not clear when the @ is required and when it’s not. Based on my testing, the behavior seems inconsistent:
-
This works fine without
@:[MapProperty(nameof(Car.Manufacturer.Name), nameof(CarDto.ManufacturerName))] -
But this fails with a compile-time error:
[MapProperty(nameof(Car.Manufacturer.ManufacturerKind.Name), nameof(CarDto.ManufacturerKind))]Error:
Specified member ManufacturerKind.Name on source type MapperlyTest.Car was not found. -
Adding
@fixes it:[MapProperty(nameof(@Car.Manufacturer.ManufacturerKind.Name), nameof(CarDto.ManufacturerKind))]
It seems that the @ is required when the property path has three or more segments, but not when it has only two. This behavior is not documented and feels unintuitive.
Could you please either:
- Clarify and update the documentation to explain exactly when the
@is required, or - Consider fixing this inconsistency so that the
@is not needed at all (or always required), for consistency and better developer experience.
Reported relevant diagnostics
- RMG006
Environment:
- Mapperly Version: 4.2.1
- Nullable reference types: enabled
- .NET Version: 8.0.18
- Target Framework: net8.0
- Compiler Version: 4.14.0-3.25279.5 (995f12b6)
- C# Language Version: 12.0
- IDE: Visual Studio v17.14.5
- OS: Windows 11
The @ prefix is required when specifying a property path (e.g. Make.Id) rather than a single property (e.g. Id). Without the @, the expression is interpreted using the default C# nameof behavior. By using @, you explicitly enable Mapperly’s full-nameof feature, which builds a property path based on the entire nameof expression.
To resolve the correct property path, Mapperly ignores the namespace and class name(s), but includes all subsequent segments.
Examples
Without full-nameof: nameof(MyNamespace.Car.Make.Id) → "Id"
With full-nameof: nameof(@MyNamespace.Car.Make.Id) → "Make.Id"
The documentation will be improved in https://github.com/riok/mapperly/pull/1894.
If you have suggestions for further clarification, please let me know, otherwise, I hope this resolves the confusion.
@latonz thanks for improving documentation.
But if @ sign is always required for complex properties, why is the following mapping working correctly without @ sign?
[MapProperty(nameof(Car.Manufacturer.Name), nameof(CarDto.ManufacturerName))]
nameof(Car.Manufacturer.Name) -> "Name"
But the generate code is:
if (car.Manufacturer != null)
{
target.ManufacturerName = car.Manufacturer.Name;
}
Isn't there a warning reported that Name couldn't be found?
It still works due to Mapperly's auto-flattening.
It still works due to Mapperly's auto-flattening.
That was my first idea, but such mapping also works:
[MapProperty(nameof(Car.YYY.Bzzz), nameof(CarDto.RamambuHaraMambaRo))]
public static partial CarDto MapCarToDto(Car car);
The generated code is correct:
if (car.YYY != null)
{
target.RamambuHaraMambaRo = car.YYY.Bzzz;
}
I was thinking that maybe Mapperly is looking for the property Bzzz across the full hierarchy, and added the two properties with the same type to "fool" this search:
public class Car
{
public Manufacturer? YYY { get; set; }
public Manufacturer? OneMoreManufacturer { get; set; }
}
But the generated code is still correct...
You are absolutely right, and this is indeed a bug. I'll look into how we can fix it without causing significant disruptions to the existing code.
@latonz thanks for solving this!