-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Description
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?