Guess what? It's a piece of cake 🍰
A boilerplate was built using C# Analyzers for object-to-object mapping in DotNET.
Beacuase this project was developed using the standard Roslyn Source Generators, you won't need to consider any configuration codes in your setup classess.
You only need to:
- add the library in your project's refrences
- decorate your destination classes with our valid attributes.
🤔 But wait! what about the more complex conversions? Don't worry. We considered a way of injecting your out-of-box mapping rules whenever you want to use the mapping.
-
via
dotnet-cli
:dotnet add package Mapalyzer --version 1.3.1-alpha
-
via NuGet console:
Install-Package Mapalyzer -Version 1.3.1-alpha
-
via .csproj
<PackageReference Include="Mapalyzer" Version="1.3.1-alpha" />
- If using Visual Studio, you must clean the solution, restart the Visual Studio and rebuild the solution in order to get the library to work.
- If using VSCode, you must execute
dotnet restore && dotnet build
and restart VSCode in order to get the library to work.
🚢 Now the ship is ready to sail. You just need to decorate your desired destination classes and/or their properties in order to connect them to your source classes.
The MapFrom attribute is used to decorate the destination class and accepts an argument which is a Type
value of the source class. As soon as you decorate the class, the analyzer of the library seeks the source class and mapps all twin properties implicitly.
Then the mapping code is being generated in background and added to the assembly of your project.
Note that since all mapping methods are being defined under the assembley of the project, you don't need to import any references whenever you want to use them.
For instance, consider you have a class named Entity
in our domain layer which is specified as database object and another class in our application layer named EntityModel
. After a successful mapping, whenever you declare an Entity
, you immediately and easily will access an extension method called ToEntityModel
which returns an EntityModel
object.
Plus, we also considered a way of custom mapping.
The destination class should have the partial
modifier beacuase the mapper will generated a partial class with the same namespace and name. If your desired destination class has not the partial
modifier, there will be not mapping class.
Then you need to decorate the destination class with the MapFrom
attribute which you can check out the attribute's signature in the following table.
If there are any properties in destination class which its signature is different from all properties of the source class and you still want to map it with one of them, you can easily decorate your desired destination property with the MapPropertyFrom
attribute.
Consider you have your own rules for mapping a specific model. How would you do that? Don't worry! In this case, you can define a method which returns an object with the type Action<SourceClassType, DestinationClassType>
. Then you can pass it as an delegate argument to extension methods(check out the sample below).
Name | Parameters | Description |
---|---|---|
MapFrom | serviceType: Type |
The type value of the source class. ex: typeof(Your.Core.Project.Domain.Entity) |
MapPropertyFrom | sourcePropertyName: string ignoreTypeDifference: bool |
sourcePropertyName : The name of the desired property in the source class.ignoreTypeDifference : The default is false. If you set it as true , while the type of the both properties implemented IConvertible , the source property will be convert to the destination property. |
namespace Your.Core.Project.Domain
{
public class Entity
{
// Your source properties definitions
// ...
public string Buz { get; set; }
public byte Qux { get; set; }
// Consider it holds multiple values separated by a ',' like "43,2,289,13"
public string Bar { get; set; }
}
}
using System;
// All attributes you're going to use are being generated under the System namespace \
// which is why there is no need to reference to any namespaces but System.
using System.Linq;
namespace Your.Application.Project.Models
{
// Don't forget to add partial modifier to the class
[MapFrom(sourceType: typeof(Your.Core.Project.Domain.Entity))]
public partial class EntityModel
{
// Your destination properties definitions which are being implicity mapped \
// because the mapper found their twins in source class
// ...
// You can enforce the mapper to map a specific destination property \
// differing either in name or type with another decorator.
[MapPropertyFrom(sourcePropertyName: "Buz")]
public string Foo { get; set; }
[MapPropertyFrom(sourcePropertyName: "Qux", ignoreTypeDifference: true)]
public short Qux { get; set; }
// It will not mapped beacuse there is no corresponding source property to match exactly with its signature. Now we need to map it in our CustomMapper.
public short[] Bar { get; set; }
// Custom Mapper
public static Action<Your.Core.Project.Domain.Entity, EntityModel> CustomMapper() => (entity, entityModel) =>
{
entityModel.Bar = entity.Split(",", StringSplitOptions.RemoveEmptyEntries)
.Select(b => Convert.ToInt16(b))
.ToArray();
// Other custom rules
// ...
}
}
}
📧 E-mail: [email protected]