简体   繁体   中英

Automapper and immutability

Is it possible to use AutoMapper with Immutable types?

For example my Domain type is immutable and I want to map my view type to this.

I believe it is not but just want this confirmed.

Also as it is best practice to have your domain types immutable, what is the best practice when mapping your view types to domain types?

I typically do the mapping from view types to domain types by hand, as I'll typically be working through a more complex interface, using methods and so on. If you use AutoMapper to go from view to domain, you're now locked in to an anemic domain model, whether you've intentionally decided to or not.

Suppose that you really did want an immutable property on your Domain type, say Id . Your domain type might look something like this:

public class DomainType
{
    public DomainType(int id)
    {
        Id = id;
    }

    public int Id { get; }
    // other mutable properties
    // ...
}

Then you can use ConstructUsing using a public constructor of your choice, such as:

CreateMap<ViewType, DomainType>()
    .ConstructUsing(vt => new DomainType(vt.Id));

Then map all the mutable properties in the normal way

AutoMapper relies on property setters to do its work, so if you have read-only properties, AutoMapper won't be of much use.

You could override the mapping behaviour and, for example, configure it to invoke a specific constructor, but that basically defeats the purpose of AutoMapper because then you are doing the mapping manually, and you've only succeeded in adding a clumsy extra step in the process.

It doesn't make a lot of sense to me that your domain model is immutable. How do you update it? Is the entire application read-only? And if so, why would you ever need to map to your domain model as opposed to from ? An immutable domain model sounds... pretty useless.

PS I'm assuming that you mean this AutoMapper and not the auto-mapping feature in Fluent NHibernate or even some other totally different thing. If that's wrong then you should be more specific and add tags for your platform/language.

We have immutable objects using the builder pattern. Mapping them takes a little more boilerplate code, but it is possible

// ViewModel
public class CarModel : IVehicleModel 
{
    private CarModel (Builder builder)
    {
        LicensePlate = builder.LicensePlate;
    }    

    public string LicensePlate { get; }

    //
    public Builder
    {
        public string LicensePlate { get; set; }
    }
}


// Model
public class CarViewModel : IVehicleViewModel
{
    private CarViewModel (Builder builder)
    {
        LicensePlate = builder.LicensePlate ;
    }    

    public ILicensePlate LicensePlate { get; }

    //
    public Builder
    {
        public ILicensePlate LicensePlate { get; set; }
    }
}

Our AutoMapper Profiles have three mappings registered:

        CreateMap<IVehicleModel, CarViewModel.Builder>();
        CreateMap<CarViewModel.Builder, IVehicleViewModel>().ConvertUsing(x => x.Build());
        CreateMap<IVehicleModel, IVehicleViewModel>().ConvertUsing<VehicleModelTypeConverter>();

The VehicleModelTypeConverter then defines a two stage conversion:

    public IVehicleViewModel Convert(IVehicleModel source, IVehicleViewModel destination,
        ResolutionContext context)
    {
        var builder = context.Mapper.Map<CarViewModel.Builder>(source);
        var model = context.Mapper.Map<IVehicleViewModel>(builder);

        return model;
    }

(An implementation of ITypeListConverter<string, ILicensePlate> carries out that mapping).

Usage in our system is as normal:

var result = _mapper<IVehicleViewModel>(_carModel);

This is using AutoMapper v7.0.1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM