Proposal: Bidirectional mapping syntax:
At the moment if I want to map one object to another and back I'll write mappers as follows
public ObjectOne ToObjectOne(ObjectTwo obj)
=> new ObjectOne
{
Id = obj.Id,
Name = obj.Name
//etc
}
public ObjectTwo ToObjectTwo(ObjectOne obj)
=> new ObjectTwo
{
Id = obj.Id,
Name = obj.Name
//etc
}
I propose a bidirectional mapping syntax which would look like:
public ObjectOne ToObjectOne <=> ObjectTwo ToObjectTwo
{
ObjectOne.Id = ObjectTwo.Id,
//etc.
}
In my mind, this syntax would compile to implicitly define the ToObjectOne and ToObjectTwo methods above. Although there could be issues with this kind of syntax, i.e. mapping readonly properties and complex object to simple type mapping, these can be worked around. For instance, readonly properties are only mapped one way and complex object mappings could require a reverse mapping function.
Smells like metaprogramming/templates are needed to reduce boilerplate code.
I feel like this is too niche to modify the C# language for it. Also dislike <=>
.
The problem of mapping is too complex to be solved by simple equality syntax sugar, and bidirectional mapping at one time is an even more complex.
The real-world mappings I do always involve some kind of transformation. Sometimes I need to change data types, restrict or round numeric values, convert nulls back and forth to flag values such as -1 or double.NaN, normalize or denormalize a hierarchical model, perform external lookups (one type stores a reference to something and the mapped type stores the ID to lookup for mapping back), etc.
I would like a clean way to describe mappings and special language syntax might help. I think the <=>
symbol is fine, but I would probably use it in the definition as well to indicate that each element is mapped bidirectionally and not just one-way (which =
means):
```c#
public ObjectOne ToObjectOne <=> ObjectTwo ToObjectTwo
{
ObjectOne.Id <=> ObjectTwo.Id,
//etc.
}
But then this thing kind of lives on its own, right? The `<=>` would have no use outside of the context of a mapper. I don't even know if it makes much sense to do many other C# things inside this area. Function application is a one-way process. So are explicit casting and most expressions and statements in the language. You are pretty much limited to specifying a read/write field and a get/set property on each side of the `<=>`... this limits you to mainly same-type value copy (or reference copy) and implicit casts (which must work both ways).
So I'm left with thinking this has little to do with C#, and would probably be better done as an external DSL. And once we go down that route, we aren't restricted by C# and we can reuse it for other .NET languages.
```c#
// Definitions using the mapping DSL
map ObjectOne = ObjectTwo
Id = Id
Name = Name using NameMap
```c#
// Transformation code written in C#, assuming ObjectTwo.Name uses a Name data type
public class NameMap : Mapper
public override Name LeftToRight(string input) => new Name(input);
public override string RightToLeft(Name input) => input?.ToString() ?? string.Empty;
}
``` c#
// Use the mapper
...
var o1 = new ObjectOne(...);
var o2 = Mapper.Map<ObjectTwo>(o1);
...
There are existing libraries and fluent DSLs for similar purposes such as AutoMapper, though it is primarily designed for one-way mapping. I don't really know of any two-way mappers in .NET... quite possibly because the concept is very difficult and would require an extensive amount of complex code to the point that it's hard to see why a DSL would be better than plain C# for most real-world cases.
Most helpful comment
The problem of mapping is too complex to be solved by simple equality syntax sugar, and bidirectional mapping at one time is an even more complex.
The real-world mappings I do always involve some kind of transformation. Sometimes I need to change data types, restrict or round numeric values, convert nulls back and forth to flag values such as -1 or double.NaN, normalize or denormalize a hierarchical model, perform external lookups (one type stores a reference to something and the mapped type stores the ID to lookup for mapping back), etc.
I would like a clean way to describe mappings and special language syntax might help. I think the
<=>
symbol is fine, but I would probably use it in the definition as well to indicate that each element is mapped bidirectionally and not just one-way (which=
means):```c#
public ObjectOne ToObjectOne <=> ObjectTwo ToObjectTwo
{
ObjectOne.Id <=> ObjectTwo.Id,
//etc.
}
```c# {
// Transformation code written in C#, assuming ObjectTwo.Name uses a Name data type
public class NameMap : Mapper
public override Name LeftToRight(string input) => new Name(input);
public override string RightToLeft(Name input) => input?.ToString() ?? string.Empty;
}
There are existing libraries and fluent DSLs for similar purposes such as AutoMapper, though it is primarily designed for one-way mapping. I don't really know of any two-way mappers in .NET... quite possibly because the concept is very difficult and would require an extensive amount of complex code to the point that it's hard to see why a DSL would be better than plain C# for most real-world cases.