diff --git a/README.md b/README.md index dde6dc5..0f21dd8 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,11 @@ # MapTo -![Nuget](https://img.shields.io/nuget/v/mapto?logo=nuget) +[![Nuget](https://img.shields.io/nuget/v/mapto?logo=nuget)](https://www.nuget.org/packages/MapTo/) ![Publish Packages](https://github.com/mrtaikandi/MapTo/workflows/Publish%20Packages/badge.svg) A convention based object to object mapper using [Roslyn source generator](https://github.com/dotnet/roslyn/blob/master/docs/features/source-generators.md). -MapTo creates mappings during compile-time, which eliminates the need for using reflection to create the mappings. This makes it super simple to use and way faster than other libraries. +MapTo is a library to programmatically generate the necessary code to map one object to another during compile-time. It creates mappings during compile-time, eliminating the need to use reflection to map one object to another and make it simpler to use faster than other libraries at runtime. + ## Installation ``` @@ -12,7 +13,7 @@ dotnet add package MapTo --prerelease ``` ## Usage -To generate the mappings, simply declare the destination class as `partial` and annotate it with `MapFrom` attribute. +MapTo relies on a set of attributes to instruct it on how to generate the mappings. To start, declare the destination class as `partial` and annotate it with `MapFrom` attribute. As its name implies, `MapFrom` attribute tells the library what the source class you want to map from is. ```c# using MapTo; @@ -32,71 +33,59 @@ namespace App.ViewModels } ``` -## Generated Source -In the above example, if `App.Data.Models.User` class is defined as: - -```c# -namespace App.Data.Models -{ - public class User - { - public User(int id) => Id = id; - - public int Id { get; } - - public string FirstName { get; set; } - - public string LastName { get; set; } - - public string FullName => $"{FirstName} {LastName}"; - } -} -``` - -It will generate the following code: - -```c# -// -using System; - -namespace App.ViewModels -{ - public partial class UserViewModel - { - public UserViewModel(App.Data.Models.User user) - { - if (user == null) throw new ArgumentNullException(nameof(user)); - - FirstName = user.FirstName; - LastName = user.LastName; - } - - public static UserViewModel From(App.Data.Models.User user) - { - return user == null ? null : new UserViewModel(user); - } - } - - public static partial class UserToUserViewModelExtensions - { - public static UserViewModel ToUserViewModel(this App.Data.Models.User user) - { - return user == null ? null : new UserViewModel(user); - } - } -} -``` - -Which makes it possible to get an instance of `UserViewModel` from one of the following ways: +To get an instance of `UserViewModel` from the `User` class, you can use any of the following methods: ```c# var user = new User(id: 10) { FirstName = "John", LastName = "Doe" }; -var vm = user.ToUserViewModel(); +var vm = user.ToUserViewModel(); // A generated extension method for User class. // OR -vm = new UserViewModel(user); +vm = new UserViewModel(user); // A generated contructor. // OR -vm = UserViewModel.From(user); +vm = UserViewModel.From(user); // A generated factory method. +``` + +> Please refer to [sample console app](https://github.com/mrtaikandi/MapTo/tree/master/test/TestConsoleApp) for a more complete example. + +## Available Attributes +### IgnoreProperty +By default, MapTo will include all properties with the same name (case-sensitive), whether read-only or not, in the mapping unless annotating them with the `IgnoreProperty` attribute. +```c# +[IgnoreProperty] +public string FullName { get; set; } +``` + +### MapProperty +This attribute gives you more control over the way the annotated property should get mapped. For instance, if the annotated property should use a property in the source class with a different name. + +```c# +[MapProperty(SourcePropertyName = "Id")] +public int Key { get; set; } +``` + +### MapTypeConverter +A compilation error gets raised by default if the source and destination properties types are not implicitly convertible, but to convert the incompatible source type to the desired destination type, `MapTypeConverter` can be used. + +This attribute will accept a type that implements `ITypeConverter` interface. + +```c# +[MapFrom(typeof(User))] +public partial class UserViewModel +{ + public DateTimeOffset RegisteredAt { get; set; } + + [IgnoreProperty] + public ProfileViewModel Profile { get; set; } + + [MapTypeConverter(typeof(IdConverter))] + [MapProperty(SourcePropertyName = nameof(User.Id))] + public string Key { get; } + + private class IdConverter : ITypeConverter + { + public string Convert(int source, object[] converterParameters) => $"{source:X}"; + } +} ``` \ No newline at end of file