| description | Object mapping made easy |
|---|
Automatic object mapping is useful whenever you need to convert from type A to type B. Instead of manually assigning each member one at a time, an object mapper does this in a very generic way using reflection. For example, you can convert an entity type (such as Product) to a view model type (such as ProductModel) or vice-versa. Smartstore comes with object mapping utilities that are ultra-lightweight and fast.
| Utility | Description | Call |
|---|---|---|
| MiniMapper | Very simple object mapper | MiniMapper.Map(From, To) |
| MapperFactory | Advanced, customizable and flexible mapper | MapperFactory.MapAsync(From, To) |
The MiniMapper is a lightweight and simple object mapping utility that attempts to map properties of the same name between two objects. It uses reflection under the hood to query and access properties. If matched properties have different types, the mapper tries to convert them using the type conversion system. If this fails, the property is simply skipped and no exception is thrown.
{% hint style="success" %}
Use MiniMapper when:
- source and target type look more or less the same
- you don't need full control over mapping {% endhint %}
{% hint style="warning" %}
MiniMapper cannot deep-copy collection types and does not clone reference types.
{% endhint %}
BlogSettings settings = /* get instance somehow */;
// Maps BlogSettings --> BlogSettingsModel (must have a parameterless constructor)
// and returns the destination instance
var model = MiniMapper.Map<BlogSettings, BlogSettingsModel>(settings);
// Maps BlogSettings --> BlogSettingsModel by populating the given target instance.
var model = new BlogSettingsModel();
MiniMapper.Map(settings, model);The MapperFactory is a static factory that can create custom type mapper instances (IMapper<TFrom, TTo>).
{% hint style="success" %}
Use MapperFactory when:
- source and target type are very different
- you need more control over mapping {% endhint %}
Upon initialization, MapperFactory automatically scans for all concrete IMapper<TFrom, TTo> classes in all loaded assemblies. Because the mapper is DI-enabled it can depend on any registered service.
You can register multiple mappers for the same type pair without any conflicts. When a mapping operation is requested, the MapperFactory will combine all the registered mappers into a composite mapper. This composite mapper will internally delegate the object mapping to each individual mapper, ensuring that all the registered mappers contribute to the final mapping result.
| Call | Description |
|---|---|
MapperFactory.GetMapper<TFrom, TTo>() | Resolve a mapper instance (either single, composite or generic) |
GetRegisteredMapper<TFrom, TTo>() | Resolve a mapper instance or return null if no mapper is registered for the given type pair. |
Map*() | Map object instances (dynamic parameters are allowed) |
{% hint style="info" %}
If no mapper is found for a specific mapping operation, a generic mapper is used, which internally delegates object mapping to MiniMapper.
{% endhint %}
To add a mapper using MapperFactory, create a class that implements IMapper<TFrom, TTo>. There is nothing wrong with having more than one interface implemented in a single class (e.g. IMapper<News, NewsModel> and IMapper<NewsModel, News>). There is no need for the mapper to be registered in DI.
{% hint style="info" %} It is good practice to keep the model and mapper classes in the same file. {% endhint %}
public class NewsMapper :
IMapper<News, NewsModel>,
IMapper<NewsModel, News>
{
public NewsItemMapper()
{
// Pass dependencies if required
}
public Task MapAsync(News from, NewsModel to, dynamic parameters = null)
{
// ... map News --> NewsModel ...
return Task.CompletedTask;
}
public Task MapAsync(NewsModel from, News to, dynamic parameters = null)
{
// ... map NewsModel --> News ...
return Task.CompletedTask;
}
}
The following code shows different ways to map an entity to a model.
News entity = /* get entity instance somehow */;
NewsModel model = new NewsModel();
// Get the mapper = an instance of "NewsMapper" above,
// because "NewsMapper" implements "IMapper<News, NewsModel>"
var mapper = MapperFactory.GetMapper<News, NewsModel>();
// Map News --> NewsModel
await mapper.MapAsync(entity, model);
// - OR, short way without resolving the mapper first -
await MapperFactory.MapAsync(entity, model);
// Map News --> NewsModel with parameters
await mapper.MapAsync(entity, model, new { Option1 = true, Option2 = "stuff" });