0

I have the following two ShopifySharp objects.

public class Product
{            
    public string Title { get; set; }

    public string BodyHtml { get; set; }

    public IEnumerable<ProductVariant> Variants { get; set; }
}


public class ProductVariant 
{
    public string Title { get; set; }

    public string Barcode { get; set; }

}

I then have the following model

public class ShopifyVariant 
{
    public long Id { get; set; }

    public long ProductId { get; set; }

    public string ProductName { get; set; }

    public string VariantName { get; set; }

    public string Price { get; set; }
}

I want to map an instance of ShopifySharp.Product to IEnumerable<ShopifyVariant> because each ShopifySharp.Product will always have have AT LEAST a single ProductVariant.

Usually this would be a trivial problem because you could simply create a map between the two objects along the lines of:

this.CreateMap<ShopifySharp.ProductVariant, ShopifyVariant>()

BUT I need to get the ProductName and ProductId for each variant which can only be obtained from the ShopifySharp.Product object.

My initial thought was to try something like this

this.CreateMap<ShopifySharp.ProductVariant, ShopifyVariant>()
.ForMember(o => o.Id, o => o.MapFrom(f => f.Id))
.ForMember(o => o.ProductId, o => o.MapFrom((src, dest, m, c) => c.Items["ProductId"]))
.ForMember(o => o.ProductName, o => o.MapFrom((src, dest, m, c) => c.Items["ProductName"]))
.ForMember(o => o.VariantName, o => o.MapFrom(f => f.Title));

this.CreateMap<ShopifySharp.Product, IEnumerable<ShopifyVariant>>()

But it was unclear to me how to actually create the projection from Product to IEnumerable.

I'm currently playing with using a Custom ITypeConverter but haven't worked it out yet.

Can anyone help? How do you map a single instance of an object to a collection of reduced entities?

Maxim Gershkovich
  • 45,951
  • 44
  • 147
  • 243
  • Use what you have and just map Product.Variants, not Product. The MapFrom for Id is not needed. – Lucian Bargaoanu Sep 07 '19 at 08:46
  • Thanks for the suggestion, but if I understand you correctly I am not sure it will help, keep in mind that my final goal is to be able to pass in a IEnumerable and receive an IEnumerable for all products and all variants. So I definitely need some way to pass the Id and Title (without doing it manually) to achieve this. – Maxim Gershkovich Sep 07 '19 at 08:50
  • Perhaps write a LINQ that does what you want and then try to see if you can come up with a simple enough AM config that does the same. Or combine the two. Start with some LINQ and _then_ use AM. – Lucian Bargaoanu Sep 07 '19 at 08:57
  • Try `CreateMap>().ConvertUsing((product, destination, context)=> context.Mapper.Map>(product.Variants))`. And set the parameters in the `Map` call. – Lucian Bargaoanu Sep 07 '19 at 10:02

2 Answers2

1

As I've already said in the comments, some LINQ would help a lot here. But here it is. You can apply the same idea to your version. Most of the MapFroms you have are useless.

c.CreateMap<ProductVariant, ShopifyVariant>()
    .ForMember(o => o.ProductId, o => o.MapFrom((src, dest, m, context) => context.Items["ProductId"]))
    .ForMember(o => o.ProductName, o => o.MapFrom((src, dest, m, context) => context.Items["ProductName"]))
    .ForMember(o => o.VariantName, o => o.MapFrom(f => f.Title));
c.CreateMap<Product, IEnumerable<ShopifyVariant>>().ConvertUsing((product,variant,context)=>
{
    context.Items["ProductId"] = product.Id;
    context.Items["ProductName"] = product.Name;
    return context.Mapper.Map<List<ShopifyVariant>>(product.Variants);
});

new []{new Product{Id=1,Variants=new[]{new ProductVariant{Id=1},new ProductVariant{Id=2}}}, new Product{Id=2,Variants=new[]{new ProductVariant{Id=3},new ProductVariant{Id=4}}}}
.SelectMany(product=>mapper.Map<List<ShopifyVariant>>(product,_=>{}));

The IEnumerable map can be omitted and replaced with the same code inline, when calling Map.

Lucian Bargaoanu
  • 3,336
  • 3
  • 14
  • 19
  • Unfortunately this approach doesnt seem to work as AutoMapper still requires a mapping between Product and ProductVariant and will throw an exception if not provided. I will review this in detail later and post my results. Thanks for your help – Maxim Gershkovich Sep 08 '19 at 07:43
  • No, not really. I tested this mapping. Unless you're doing smth you didn't show. – Lucian Bargaoanu Sep 08 '19 at 07:59
-1

I have solved the problem (suboptimally) using the following mappings.

this.CreateMap<ShopifySharp.ProductVariant, ShopifyVariant>().ForMember(o => o.Id, o => o.MapFrom(f => f.Id))
.ForMember(o => o.ProductId, o => o.MapFrom(f => f.ProductId))
.ForMember(o => o.VariantName, o => o.MapFrom(f => f.Title))
.ForMember(o => o.Barcode, o => o.MapFrom(f => f.Barcode))
.ForMember(o => o.Price, o => o.MapFrom(f => f.Price))
.ForMember(o => o.Grams, o => o.MapFrom(f => f.Grams))
.ForMember(o => o.Taxable, o => o.MapFrom(f => f.Taxable))
.ForMember(o => o.Weight, o => o.MapFrom(f => f.Weight))
.ForMember(o => o.WeightUnit, o => o.MapFrom(f => f.WeightUnit))
.ForMember(o => o.SKU, o => o.MapFrom(f => f.SKU));

this.CreateMap<ShopifySharp.Product, ShopifyVariant>().ForMember(o => o.ProductName, o => o.MapFrom(f => f.Title));

this.CreateMap<IEnumerable<ShopifySharp.Product>, IEnumerable<ShopifyVariant>>()
.ConvertUsing<ShopifyProductsToVariantsCollectionTypeConverter>();

internal class ShopifyProductsToVariantsCollectionTypeConverter : ITypeConverter<IEnumerable<ShopifySharp.Product>, IEnumerable<ShopifyVariant>>
{
    public IEnumerable<ShopifyVariant> Convert(IEnumerable<ShopifySharp.Product> source, IEnumerable<ShopifyVariant> destination, ResolutionContext context)
    {
        var result = new List<ShopifyVariant>();
        foreach (var product in source)
        {
            foreach (var variant in product.Variants)
            {
                var mappedVariant = context.Mapper.Map<ShopifyVariant>(variant);
                context.Mapper.Map(product, mappedVariant);
                result.Add(mappedVariant);
            }
        }

        return result;
    }
}

Would love to see a better approach.

Maxim Gershkovich
  • 45,951
  • 44
  • 147
  • 243