Skip to content

Enhancement: Map Part Of Configuration From Existing Operators #4246

@biqas

Description

@biqas

Hello,

It would be nice if the mapper could take some configuration parts from exiting implicit or explicit conversion operators defined on the type.

The code below is just a simple illustration.

Sourcetypes

public interface IMap<TSource, TTarget>
{
    abstract static Expression<Func<TTarget>> Map(TSource entity);
}

public record TenantDto : IMap<TenantEntity, TenantDto>
{
    public Guid UID { get; init; }
    public string Name { get; set; }
    public MetaDataDto MetaData { get; init; }
    public UserDto[] Users { get; init; } = Array.Empty<UserDto>();

    public static explicit operator TenantDto(TenantEntity entity)
    {
        return Map(entity).Compile().Invoke();
    }

    public static Expression<Func<TenantDto>> Map(TenantEntity entity)
    {
        return () => new TenantDto
        {
            UID      = entity.UID,
            Name     = entity.CanonicalName,
            MetaData = (MetaDataDto)entity.MetaData,
            Users    = entity.Users.Select(x => (UserDto)x).ToArray()
        };
    }
}

public record MetaDataDto
{
    public DateTimeOffset CreatedOn { get; init; }
    public string CreatedBy { get; init; }

    public static explicit operator MetaDataDto(MetaDataEntity entity)
    {
        return new MetaDataDto
        {
            CreatedOn = entity.CreatedOn,
            CreatedBy = entity.CreatedBy
        };
    }
}

public record UserDto
{
    public Guid UID { get; init; }
    public Guid TenantUID { get; init; }
    public TenantDto? Tenant { get; init; }
    public MetaDataDto MetaData { get; init; }

    public static explicit operator UserDto(UserEntity entity)
    {
        return new UserDto
        {
            UID       = entity.UID,
            TenantUID = entity.TenantUID,
            Tenant    = entity.Tenant is null ? default : (TenantDto)entity.Tenant,
            MetaData  = (MetaDataDto)entity.MetaData,
        };
    }
}

Destination types

public record TenantEntity
{
    public Guid UID { get; init; }
    public MetaDataEntity MetaData { get; init; }
    public HashSet<UserEntity> Users { get; init; } = new();
}

public record MetaDataEntity
{
    public DateTimeOffset CreatedOn { get; init; }
    public string CreatedBy { get; init; }
}

public record UserEntity
{
    public Guid UID { get; init; }
    public Guid TenantUID { get; init; }
    public TenantEntity? Tenant { get; init; }
    public MetaDataEntity MetaData { get; init; }
}

Mapping configuration

cfg.CreateMap<TenantDto, TenantEntity>();
cfg.CreateMap<MetaDataDto, MetaDataEntity>();
cfg.CreateMap<UserDto, UserEntity>();

cfg.CreateMap<TenantEntity, TenantDto>()
    .ForMember(dest => dest.Users, opt => opt.MapFrom(src => src.Users.ToArray()));

cfg.CreateMap<MetaDataEntity, MetaDataDto>();
cfg.CreateMap<UserEntity, UserDto>();

Desired behavior

cfg.CreateMap<TenantDto, TenantEntity>();
cfg.CreateMap<MetaDataDto, MetaDataEntity>();
cfg.CreateMap<UserDto, UserEntity>();

cfg.CreateMap<TenantEntity, TenantDto>();

cfg.CreateMap<MetaDataEntity, MetaDataDto>();
cfg.CreateMap<UserEntity, UserDto>();

Ideally Desired behavior

No Config :)

The mapping from TenantEntity to TenantDto could be taken from 'public static Expression<Func> Map(TenantEntity entity)'

The benefit for such addition would be that other parts of the code could still use the natural operator with same logic.
Besides this the configuration could be made completely invisible if all the involved types would provide the mapping in such a way.
It is not necessary to implement from such an interface: 'IMap<TSource, TTarget>' it could be also convention based or attributed.

And the essence of operators is to convert from one type to another or not? Why not to reuse them?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions