Mapster icon indicating copy to clipboard operation
Mapster copied to clipboard

Struggling with mapping readonly ICollection property

Open akordowski opened this issue 1 year ago • 21 comments

Discussed in https://github.com/MapsterMapper/Mapster/discussions/688

Originally posted by akordowski March 24, 2024 Hi everyone!

I am currently struggling with the mapping of a readonly ICollection property. I followed the instructions but I can't make it work. The readonly collection is not populated with the mapped content. What am I making wrong? Have anyone an idea how I can make it work?

Here the code:

Programm.cs

var config = TypeAdapterConfig.GlobalSettings;
config.Scan(typeof(ChannelMapping).Assembly);
config.Default.UseDestinationValue(m => m.SetterModifier == AccessModifier.None &&
                                        m.Type.IsGenericType &&
                                        m.Type.GetGenericTypeDefinition() == typeof(ICollection<>));

var channelSrc = new MapsterTest.Objects.Source.Channel
{
    ChannelId = "123",
    Thumbnails = new MapsterTest.Objects.Source.ThumbnailDetails
    {
        Default = new MapsterTest.Objects.Source.Thumbnail
        {
            Url = "https://www.youtube.com/default.jpg"
        },
        Medium = new MapsterTest.Objects.Source.Thumbnail
        {
            Url = "https://www.youtube.com/medium.jpg"
        },
        High = new MapsterTest.Objects.Source.Thumbnail
        {
            Url = "https://www.youtube.com/high.jpg"
        }
    }
};

// Thumbnails are mapped correctly to a collection
var thumbnailsDest = channelSrc.Thumbnails.Adapt<ICollection<MapsterTest.Objects.Destination.Thumbnail>>().ToList();

// channelDest.Thumbnails collection is empty
var channelDest = channelSrc.Adapt<MapsterTest.Objects.Destination.Channel>();

ThumbnailMapping.cs

public class ThumbnailMapping : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        config.ForType<Objects.Source.ThumbnailDetails, ICollection<Objects.Destination.Thumbnail>>()
            .MapWith(src => MapThumbnailDetails(src).ToList());
    }

    private static IEnumerable<Objects.Destination.Thumbnail> MapThumbnailDetails(Objects.Source.ThumbnailDetails thumbnailDetails)
    {
        yield return MapThumbnail(thumbnailDetails.Default, "Default");
        yield return MapThumbnail(thumbnailDetails.Medium, "Medium");
        yield return MapThumbnail(thumbnailDetails.High, "High");
    }

    private static Objects.Destination.Thumbnail MapThumbnail(
        Objects.Source.Thumbnail thumbnail,
        string thumbnailType) =>
        new()
        {
            Type = thumbnailType,
            Url = thumbnail.Url.Trim(),
        };
}

Channel.cs (Destination)

public class Channel
{
    public string ChannelId { get; set; } = default!;
    public ICollection<Thumbnail> Thumbnails { get; } = new List<Thumbnail>();
}

Thumbnail.cs (Destination)

public class Thumbnail
{
    public string Type { get; set; } = default!;
    public string Url { get; set; } = default!;
}

Channel.cs (Source)

public class Channel
{
    public string ChannelId { get; set; } = default!;
    public ThumbnailDetails Thumbnails { get; set; } = default!;
}

ThumbnailDetails.cs (Source)

public class ThumbnailDetails
{
    public Thumbnail? Default { get; set; }
    public Thumbnail? Medium { get; set; }
    public Thumbnail? High { get; set; }
}

Thumbnail.cs (Source)

public class Thumbnail
{
    public string Url { get; set; } = default!;
}

akordowski avatar Mar 29 '24 12:03 akordowski

@akordowski I have the same issue. Did you ever find a solution?

This project is starting to look abandoned — no commits since September 2023, and no release since then. And issues opened are not answered... Is it time to move on to a different mapper?

steingran avatar Aug 08 '24 11:08 steingran

This is how I solved the problem. It's far from being an optimal solution but it works. It was a special case, but it might give you an idea. Hope it helps you.

public class SrcClassMapping : IRegister
{
    public void Register(TypeAdapterConfig config)
    {
        config
            .ForType<DestClass, ICollection<SrcClass>>()
            .MapWith(src => MapCollection(src, null))
            .MapToTargetWith((src, dest) => MapCollection(src, dest));
    }

    private static ICollection<SrcClass> MapCollection(
        DestClass? destObj,
        ICollection<SrcClass>? srcCollection)
    {
        srcCollection ??= new List<SrcClass>();

        if (destObj is null)
            return srcCollection;

        // Custom mapping

        return srcCollection;
    }
}

akordowski avatar Aug 08 '24 12:08 akordowski

@akordowski Great, thanks! Will give it a try

steingran avatar Aug 09 '24 07:08 steingran