# Create Mappers

### Writing your Mapper <a href="#writing-your-mapper" id="writing-your-mapper"></a>

A class mapper **must** implement the `IMapper` interface from `Mobilize.WebMap.Common.Messaging`. The first type parameter of this generic interface should be the model. The second parameter of the generic interface is the DTO class. For information on creating your mappers see the [Creating a DTO](/webmap/general/backend/dcp-desktop-compatibly-platform/library-bundles/bundle-dto/dto-data-transfer-objects/untitled.md).

This interface has three methods:

* `Map` this method is called to create a DTO instance and populate from the model. If you do not want to send anything to the client you can return `null`
* `ApplyChanges` this method is called to `sync` back data from the client. In this method you can take data sent from the client and apply those changes to the model on the backend.
* `Configure` this method is called to setup which data elements from the model must be considered as references.

A possible mapper for the previous DTO can be like the following code:

```
    public class ButtonMapper : IMapper
    {
        public void ApplyChanges(IObservable observable, IDataTransfer dto)
        {
            var target = observable as Models.Button;
            var source = dto        as DataTransfer.Button;
            this.Map(source, target);
        }

        public void Configure(MapperInformation config)
        {
            // There is no references to configure
        }

        public object Map(IObservable observable)
        {
            var source = observable as Model.Button;
            var target = new           DataTransfer.Button();
            this.Map(source, target);
            return target;
        }

        private void Map(Models.Button observable, DataTransfer.Button dto)
        {
            dto.Text = observable.Text;
            dto.Name = observable.Name;
        }

        private void Map(DataTransfer.Button dto, Models.Button observable)
        {
            observable.Name = dto.Name;
            observable.Text = dto.Text;
        }
    }
```

### Mapper registration <a href="#using-the-observableextensions" id="using-the-observableextensions"></a>

Mappers need to be registered in a Registration class in order to be able to be accessed during the request/response execution. A mapper is specifically associated to a type, so, when each button in an application will always trigger the ButtonMapper to synchronize data.

```
catalog.AddMapper(new DmColumn());
```

### Using the ObservableExtensions <a href="#using-the-observableextensions" id="using-the-observableextensions"></a>

You can also use `using Mobilize.WebMAP.Common.Core` to include the `ObservableExtensions`.\
This will extensions add helper methods that can be used for several tasks.

#### NullableTypes <a href="#nullabletypes" id="nullabletypes"></a>

If you define properties like public `int? Value` then the null value can be used when we know that those properties do not need to be sent.

For example with this code:

```
dto.Value = model.GetChanged(x => x.Value);
```

The extension method `GetChanged` will return `null`. Null values on the current formatting settings will not be sent.

#### Getting Events or Delegates <a href="#getting-events-or-delegates" id="getting-events-or-delegates"></a>

To get information about model delegates you can use several extensions methods.

For example to get information about all model delegates (regardless of whether they have been modified on the current request or not):

```
 Dictionary events = model.GetDelegates();
```

This will return a dictionary where each key is the Event or Delegate name and the value is a boolean value indicating if those events or delegates have a handler.

You can use restrict which events or delegates you need. For example:

```
 Dictionary events = model.GetDelegates(x => x.Click, x => x.KeyPress);
```

You can also send only the events or delegates that changed during a request:

```
 Dictionary events = model.GetDelegatesChanged(x => x.Click, x => x.KeyPress);
```

#### Sending private properties <a href="#sending-private-properties" id="sending-private-properties"></a>

Mappers and DTO should be created on a different assembly that the one where the model is defined. However, that makes it impossible to access private properties.

A recommendation is to make these properties `internal` or expose them thru an `internal` property. And then add a `InternalsVisible('MappersAndDTOAssemblyName')` attribute to the models assembly. This will allow using this internals from the mappers.

#### Avoid setting properties with side effects <a href="#avoid-setting-properties-with-side-effects" id="avoid-setting-properties-with-side-effects"></a>

As a recommendation, if you have a property getter or setter that has some logic avoid using them directly and prefer to set other properties without any side effects.

#### Handling Calculated properties <a href="#handling-calculated-properties" id="handling-calculated-properties"></a>

Sometimes you cannot avoid using calculated properties. The problem with calculated properties is that the `GetChanged` extension only works on properties with the `Intercepted` attribute.

Let's see how to handle it on a hypothetical calculated property `CalculatedProperty1`. Let's say that this property depends on `PropertyA` and `Reference.PropertyB` then in your mapper you can create a method like:

```
private bool GetCalculatedProperty1Changed(SomeModel model) 
{
   if (model.GetChanged(x => x.PropertyA)!=null && model.Reference?.GetChanged(x => x.PropertyB))
   {
     return model.CalculatedProperty1;
   }
   return null;
}
```

## Creating Mappers with Factories <a href="#creating-mappers-with-factories" id="creating-mappers-with-factories"></a>

There are situations where you have types that are not completely known beforehand for example for generic types. In this case, you can create a factory class that will be used whenever a mapper is not found.

The mapper registration works only for a specific type, which, means that only if a control is of the same type that is registered it will be take part of the mapping process, although, in some cases it may be necessary to register different types to a particular mapper, in these cases WebMap provides a MapperFactory which is used to register several types to a Mapper. For example, if there is a GenericButton class that inherits from Button, by registering directly a Mapper it would only connect the Button type to it, but, if a MapperFactory is registered, both the Button and GenericButton class would go through the same mapper.

Let's say that you have a generic component `Button` then the factory will be something like:

```
    public class GenericButtonMapper
    {

        bool CanGetMapper(Type type) { returns type.IsGeneric && typeof(Button<>).IsAssignableFrom(type.GetGenericTypeDefinition());}

        /// 
        /// Gets the mapper.
        /// 
        /// The type.
        /// The type for the mapper
        MapperInformation GetMapperInformation(Type type) 
        {
             var mapperType = typeof(ButtonMapper<>).MakeGenericType(type.GetGenericArguments()[0]);
             var dtoType = typeof(ButtonDataTransfer<>).MakeGenericType(type.GetGenericArguments()[0]);
             var mapper = Activator.CreateInstance(mapperType);
             var mapperId = "btn" + type.GetGenericArguments()[0].Name;
             new MapperInformation(mapperId, type, dtoType, mapper);           
        }
    }
```

This factory will return a new `ButtonMapper` instance using `T` as **InType** and `ButtonDataTransfer` as **OutType**


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.gapvelocity.ai/webmap/general/backend/dcp-desktop-compatibly-platform/library-bundles/bundle-dto/mappers/create-mappers.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
