A .NET source generator that allows you to map from one type to another with a similar signature in a strongly typed manner. An early version of the package is available from NuGet here.
This library is very new and as such lacks many essential features and will most likely fail spectacularly if used in anything resembling production. The below list enumerates the features which I have currently identified as reasonable for the library to have.
-
B b = a.Map(); // Variable assignment
-
a.B = c.Map(); // Property assignment
-
Process(a.Map()); // Method argument
-
new B(a.Map()); // Constructor argument
-
new B<int>(a.Map(), 1); // Mapping to a non-generic constructor argument
-
new B<A>(a.Map()); // Mapping to a generic constructor argument
-
Process<int>(a.Map(), 1); // Mapping to a non-generic type
-
Process<A>(a.Map()); // Mapping to generic type
-
_a = b.Map(); // Member assignment
-
var v = new B { CValue = a.Map() }; // Object initializers
- Recursively map complex types
- Map List properties
- Map IEnumerable properties
- Map T[] properties
- Map properties where source type is subtype of destination
- Map properties where destination type is interface which source type implements
- Support mapping from a type with more fields than the recipient
- Extending the source type with other types, merging the properties into the resulting output
- Prevent error on recursion
- Handle destination types without a public default constructor
- Map to normal method return type
- Map collections
- Method invocation mapping
obj1.Method.MapInvoke(obj2);
- Property assignment on existing object
obj1.Map(obj2);
- Map properties where source type has implicit conversion to destination type
- Map properties where source type has explicit conversion to destination type
- Support mapping to and from records
This library grants all supported types a magical Map
function which allows instances of the type
to be converted to any type where each of the properties on the destination type can be matched by
a property on the source type. A matching property with have the same name and type. The method will
not exist until you type it out for the first time for a given source and destination type. If no
implementation appears it means that no mapping could be made. This either means that the types do
not support it, for example because they do not have the same properties or that the Map
function
has been used in an unsupported way. See Known Limitations for more info about the second case.
Example use:
var command = new CreateCustomerCommand(name, customerNumber);
Customer customer = command.Map();
Mapping to a type that is assignable from the source type simply returns the source instance.
The technical solution to implement the mapping is that Map
returns object of an intermediary
mapping type which has an implicit conversion operator to each recepient type.
If the source class already has a Map()
method, you cannot map from it.
You cannot map from types which are the product of another map operation. The reason for this
limitation is that source generators cannot understand code generated by source generators, neither
other generators nor itself. Since the return type of the Map
method is resolved in the second
compilation it cannot be understood by the source generator.
Since the return type of the Map
method is determined based on how it is used, the library must
handle all possible scenarios explicitly. There are bound to be edge cases which have been
forgotten. If you run into such a case then create an issue and I will look into it. See the Roadmap
sections for the supported scenarios. More scenarios may be supported by happy accident if they are
similar enough to an existing case. Also, if a mathing mapping has already been generated from another
use of the Map
method, it will become avaiable everywhere regardless if it is supported.
Mapping to values in tuple types is particularly complex, so it is unlikely to make it into 1.0.
Mapping to and from interface types is not supported, since C# does not allow you to implement custom convertions for interfaces in either direction. This limitation is specific to the root type being mapped, not the properties of child objects.
Mapping to abstract types is not directly supported. You can work around this by mapping to a type having the desired base class somewhere else in your code. This mapping can also be used for the mapping to the abstract class.
Mapping of non-generic collections is not supported since there is no way to know that the actual types of the objects are.
Mapping of multidimensional arrays are consided too niche a use case and is therefore not supported.
Anonymous types are not support, neither for the Map
nor the Extend
method.