I do not even know what an auto-chopper is. Why do I need his alternative?
Imagine the situation, you have two classes for the same entity, one describes the data model from a custom form, the second database model. The properties of these classes coincide by 95 percent, the differences can only be in some timestamps or other system fields in the database model. When a user fills out a form, you get a model from that form, and then you need to convert it to a database model to save.
FlashMapper , like AutoMapper, is a .net library that saves you from writing routine code during the conversion process. It automatically matches all the same class properties, leaving you only to resolve differences.
In addition to getting rid of routine work, this approach provides additional verification of the correctness of the code of your program. Imagine that you have added some new properties to these classes, and forgot to link them. At best, you will find out about this somewhere at the testing stage, at worst a month after the release, when you notice that you don’t have new data in the database that users diligently enter into a new field on the form. FlashMapper will either bind new properties automatically if it can, or throw an exception during the next application launch.
If there is already an avtomapper, then why make another similar library?
In the auto-mapper, I never liked his monstrous syntax. Just to connect two properties in different classes, you need to register three lambdas in the configuration in order to ignore the property - two. Also, at the last job, I often began to have a need to 'grease' the data from two classes into one. I did not find a simple solution to this problem by using standard auto-mapper tools, so I started writing an extension library for it that allows you to create mappings from two or more source classes into one, and even almost added it. But then I suddenly had the idea of a simpler syntax, and I decided to drop everything and write my library from scratch.
')
Actually, FlashMapper has a simpler and more user-friendly syntax, provides the ability to 'map' from several objects to one, and also provides an API for making each separate separate mapping configuration into its own class, into which you can forward some logic as a dependency constructor Also, according to the idea, it should have had better performance, but for some reasons, which are lower, the performance turned out to be at the level of the auto-amp.
Syntax
Imagine that we have two classes, one defines the data model that comes to the server from a custom form, the second defines the storage model in the database.
Model with custom formpublic class UserForm { public Guid? Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Town { get; set; } public string State { get; set; } public DateTime BirthDate { get; set; } public string Login { get; set; } public string Password { get; set; } }
Database model public class UserDb { public Guid Id { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string Town { get; set; } public string State { get; set; } public DateTime BirthDate { get; set; } public string Login { get; set; } public string PasswordHash { get; set; } public DateTime RegistrationTime { get; set; } public byte[] Timestamp { get; set; } public bool IsDeleted { get; set; } }
Then this is how the mapping configuration will look from first class to second:
public class FlashMapperInitializer : IInitializer
The syntax resembles the creation of a new object and the initialization of its properties based on the source object. In fact, the parameter of the CreateMapping method is a lambda expression, which is then supplemented by matching the remaining properties of the UserDb class with similar properties from the UserForm class. Also, on the basis of this expression, another one is created that does not create a new object of type UserDb, but simply copies the data into an already existing object. Both expressions are compiled and saved in mappingConfiguration for further use.
Here's how you can then use the created mapping:
public class UserController : Controller { private readonly IMappingConfiguration mappingConfiguration; private readonly IRepository<UserDb> usersRepository; public UserController(IMappingConfiguration mappingConfiguration, IRepository<UserDb> usersRepository) { this.mappingConfiguration = mappingConfiguration; this.usersRepository = usersRepository; } [HttpPost] public ActionResult Edit(UserForm model) { if (!ModelState.IsValid) return View(model); var existingUser = usersRepository.Find(model.Id); if (existingUser == null) { var newUser = mappingConfiguration.Convert(model).To<UserDb>(); usersRepository.Add(newUser); } else { mappingConfiguration.MapData(model, existingUser); usersRepository.Update(existingUser); } return View(model); } }
Mapping from multiple sources
FlashMapper allows you to create multi-source mappings (up to 15):
mappingConfiguration.CreateMapping<Source1, Source2, Source3, Destination>((s1, s2, s3) => new Destination { ... });
In this case, when automatically matching properties, suitable ones will be searched for in each source. If this property is found in several sources, FlashMapper will throw an exception that a collision has occurred. To prevent this from happening, you must either manually specify from which source the property to be taken, or specify CollisionBehavior = ChooseAny in the mapping settings.
mappingConfiguration.CreateMapping<Source1, Source2, Source3, Destination>((s1, s2, s3) => new Destination { ... }, o => o.CollisionBehavior(SelectSourceCollisionBehavior.ChooseAny));
Although this behavior is called ChooseAny, the appropriate property will not be chosen randomly, the priority depends on the source sequence number. The first will have maximum priority, the second, third, and so on.
API for making each mapping in a separate service
FlashMapper provides a set of interfaces and base classes for the configuration of each mapping in a separate class. This approach allows you to better structure the code, all configurations are not in the same method with initialization, but each in its own service. In the service, you can forward dependencies through the constructor, and thanks to this, you can use complex business logic inside the mapping.
Here is how the configuration of the mapping from the example above changes:
public interface IUserDbBuilder : IBuilder<UserForm, UserDb> { } public class UserDbBuilder : FlashMapperBuilder<UserForm, UserDb, UserDbBuilder>, IUserDbBuilder { private readonly IPasswordHashCalculator passwordHashCalculator; public UserDbBuilder(IMappingConfiguration mappingConfiguration, IPasswordHashCalculator passwordHashCalculator) : base(mappingConfiguration) { this.passwordHashCalculator = passwordHashCalculator; } protected override void ConfigureMapping(IFlashMapperBuilderConfigurator<UserForm, UserDb> configurator) { configurator.CreateMapping(u => new UserDb { Id = u.Id ?? Guid.NewGuid(), Login = u.Id.HasValue ? MappingOptions.Ignore() : u.Login, RegistrationTime = u.Id.HasValue ? MappingOptions.Ignore() : DateTime.Now, Timestamp = MappingOptions.Ignore<byte[]>(), IsDeleted = false, PasswordHash = passwordHashCalculator.Calculate(u.Password) }); } }
The IUserDbBuilder interface has two methods - UserDb Build (UserForm source) and void MapData (UserForm source, UserDb destination). The base class FlashMapperBuilder <UserForm, UserDb, UserDbBuilder> implements them, but leaves the void ConfigureMapping method for implementation (IFlashMapperBuilderConfigurator <UserForm, UserDb> configurator). This method is called during initialization and creates a mapping in the passed mappingConfiguration. Notice that the latest generic parameter of the abstract class FlashMapperBuilder should be its implementation, in this case UserDbBuilder.
Consider the configuration process in more detail. The base class FlashMapperBuilder <> also implements the IFlashMapperBuilder interface, which in turn provides the RegisterMapping method, which must be called during application initialization. As a result, the UserDbBuilder implementation must be registered in the IoC container for two interfaces - IUserDbBuilder for future use, and IFlashMapperBuilder for registration of the mapping.
Something for Ninject Kernel.Bind<IUserDbBuilder, IFlashMapperBuilder>().To<UserDbBuilder>();
To simplify this syntax, I use a small extension-method. public static IBindingWhenInNamedWithOrOnSyntax<T> AsFlashMapperBuilder<T>( this IBindingWhenInNamedWithOrOnSyntax<T> builder) where T : IFlashMapperBuilder { builder.Kernel.AddBinding(new Binding(typeof(IFlashMapperBuilder), builder.BindingConfiguration)); return builder; }
As a result, the registration of the builder in the container can be rewritten as:
Kernel.Bind<IUserDbBuilder>().To<UserDbBuilder>().AsFlashMapperBuilder();
The library has an IFlashMapperBuildersRegistrationService service, with the RegisterAllBuilders method; this method should be called during application initialization. The service simply takes all the implementations of the IFlashMapperBuilder interface, which are passed to it to the constructor, and calls the RegisterMappings method.
Here is an example of using the service IUserDbBuilder public class UserController : Controller { private readonly IUserDbBuilder userDbBuilder; private readonly IRepository<UserDb> usersRepository; public UserController(IUserDbBuilder userDbBuilder, IRepository<UserDb> usersRepository) { this.userDbBuilder = userDbBuilder; this.usersRepository = usersRepository; } [HttpPost] public ActionResult Edit(UserForm model) { if (!ModelState.IsValid) return View(model); var existingUser = usersRepository.Find(model.Id); if (existingUser == null) { var newUser = userDbBuilder.Build(model); usersRepository.Add(newUser); } else { userDbBuilder.MapData(model, existingUser); usersRepository.Update(existingUser); } return View(model); } }
In theory, there should be one problem. Let's look at the ConfigureMapping method from the configuration example. The lambda expression that describes the mapping uses the class field - passwordHashCalculator. In general, lambda expressions, like the methods resulting from their compilation, are always static. But when inside an expression you refer to some non-static class member (field, property, method), the current instance of the class that creates the expression is passed to it as a constant, and then the value of the fields of this instance is used. It turns out that the instance of the builder that was used to register the mapping will get into the mapping expression, and the values of its fields will always be used.
To prevent this from happening, the expression passed to the CreateMapping method is modified. One more parameter is added to it - the builder itself, and all calls to the current instance of the builder are replaced with a call to this parameter. The result is mapping from two parameters, the first is the data itself, the second is the builder. When the build method is later called by the builder, it takes this mapping and passes the data and itself as parameters.
It is because of this that the latest generic parameter of the FlashMapperBuilder class should be the builder itself. Of course, it would be possible to calculate this type during runtime as well, but I wanted to avoid any late binding using ready-made compiled mappings.
This approach also allows the use of multiple sources, a maximum of 14.
Performance
I was never particularly bothered by the performance of the auto-chopper, but from colleagues I heard that it runs 10 times slower than a similar manual mapping. I then decided that it is smarter than everyone, and I’ll have a performance comparable to manual mapping. It would seem that when using a ready mapping, I already have a compiled method, the IL-code of which should coincide with the IL-code of the method that performs similar work, but written by hands, there is no late binding when using mapping, you only need to get a pointer to the method and call it. But in fact, the performance of my mapper turned out to be comparable to the performance of the auto-chopper.
I was very surprised, I began to profile the code, my code did not cause any brakes, the main execution time was the compiled mapping method. I downloaded the dotnet symbols, and launched the profiler again. It turned out that 72% of the CPU time is occupied by a call to some JIT_MethodAccessCheck method.
I started googling what this method generally does. In the search results, there was a certain jithelpers.h file from the data sheets on the gihab, as well as questions on stackoverflow from people who are also concerned about the performance of compiled lambda expressions. From stackoverflow, I learned that this method is somehow related to CAS (code access security), and also found another way to compile lambda expressions. I decided to just blindly use this method, see what the performance will turn out. And, about a miracle, my method compiled from expression began to work with the same speed, as well as a similar manual method. But, unfortunately, after that two tests fell through me.
Tests on dependency injection broke. Compressed expressions have no access to the private fields of the class in which they were created. Moreover, they compiled without errors, the error popped up already at the time of conversion from one class to another. To be honest, I didn’t even suspect until this moment that I’m checking at runtime each time whether a given piece of compiled code has access to one or another member of a class.
I began to understand what was happening. The way to compile lambda expressions that have been advised on stackoverflow uses the older and lower-level API of the dotnet to generate IL code. These are the classes MethodBuilder, TypeBuilder and others. It was necessary to create them manually, define the parameters, transfer the MethodBuilder instance to the lambda expression method CompileToMethod. This method wrote down the IL-code directly of what was in the expression, so the resulting method worked quickly. After that, it was necessary to create a dynamic type from an instance of a TypeBuilder class, and to pull out of it the necessary method by reflection. It is clear that since I create a new type and the method is already defined in it, the method loses access to the private fields of the class in which the lambda expression was defined. The standard way to compile lambda expressions is a simple call to the Compile method, this method appeared in later versions of the document, and uses a newer API. This is the DynamicMethod class. It does not require hands to create dynamic types and stuff, but simply provides methods for writing IL code into it and getting a result. Although inside it also creates a new type in the same way, but it can somehow disable the checking of the availability of class members at runtime, it even has a special flag for this in the constructor. And, apparently, he adds the JIT_ MethodAccessCheck call to the compiled method.
I rummaged in the guts of the datnet, tried to circumvent the field availability check in various ways. For example, I tried to make the class created by TypeBuilder be the nested class of which I need to get access to the fields, I also tried to get access to the fields through reflection, but other problems arose there. In general, while I could not get to make money with stackoverflow, I rolled everything up to the standard compilation method.
In principle, the results are not so bad, FlashMapper, like AutoMapper, works 4 times slower than the manual method. A million conversions take 400 ms, versus 100 ms for the manual method. A simple data transfer, when new instances of the resulting class are not created, works slightly faster.

Although it is a shame, of course, that the results do not correspond to the name of the library, so I dig out a little more, maybe something will turn out.
Conclusion
At the moment, FlashMapper has a certain basic functionality of the authopper, in addition it has a more compact configuration syntax, allows mappings from several objects and has an API for making configurations into separate services.
Main library buildBuild with extensions for mapping from multiple sourcesBuilding with API for making configurations in servicesGithub sourcesDocumentation