Search code examples
asp.net-coreautomapperaspnetboilerplate

Projection or Custom Value Resolvers with new Multi Lingual Mappings


I am using the newest version of ASP.Net Boilerplate (version 3.7.2). I am currently using the new Multi-Lingual Entities.

I am having issues with the Automapping for 1 of the Entities to a Dto as not only does it require the Multi-Lingual aspect but a Projection/Custom Resolver for one of the properties.

Up to this point all the mappings have been working correctly. I have followed the documentation found at ASP.NET Boilerplate Documentation

[Table("CategoryItems")]
public class CategoryItem : BaseClass, IMultiLingualEntity<CategoryItemTranslation>
{
    /// <summary>
    /// Gets or Sets the Category Item Display Order
    /// </summary>
    public int DisplayOrder { get; set; }

    /// <summary>
    /// Gets or Sets Catalog Categories
    /// </summary>
    public virtual ICollection<CatalogItem> CatalogItems { get; set; } = new HashSet<CatalogItem>();

    ...

    /// <summary>
    /// Gets or Sets all the Entity Translations
    /// </summary>
    public virtual ICollection<CategoryItemTranslation> Translations { get; set; } = new HashSet<CategoryItemTranslation>();
}


[Table("CategoryItemTranslations")]
public class CategoryItemTranslation : IEntityTranslation<CategoryItem>
{
    /// <summary>
    /// Get or Set Category Item Name
    /// </summary>
    public string CategoryItemName { get; set; }

    /// <summary>
    /// Gets or Sets Link to Category Item
    /// </summary>
    public CategoryItem Core { get; set; }

    /// <summary>
    /// Gets or Sets Link to Core Id
    /// </summary>
    public int CoreId { get; set; }

    /// <summary>
    /// Gets or Sets the Language
    /// </summary>
    public string Language { get; set; }
}


public class ReadCategoryItemFilterDto : PhantomEntityDto
{
    public string CategoryItemName { get; set; }

    ...

    public int ItemCount { get; set; }

    ...
}

Using the Create MultiLingual Map code within the Initialize of the ApplicationModule class:

cfg.CreateMultiLingualMap<CategoryItem, CategoryItemTranslation, ReadCategoryItemFilterDto>(new MultiLingualMapContext(IocManager.Resolve<ISettingManager>()));

The CategoryItemName from the Translation Entity correctly maps to the CategoryItemName on the ReadCategoryItemFilterDto.

Now I need to map the CatalogItems Count to the ItemCount on the Dto.

I have tried adding to the Mapping Configurator:

cfg.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
    .ForMember(dest => dest.ItemCount, opts => opts.MapFrom(src => src.CatalogItems.Count));

and

cfg.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
    .ForMember(dest => dest.ItemCount, opts => opts.ResolveUsing(new CatalogItemFilterCountResolver()));

using

public class CatalogItemFilterCountResolver : IValueResolver<CategoryItem, ReadCategoryItemFilterDto, int>
{
    public int Resolve(CategoryItem source, ReadCategoryItemFilterDto destination, int m, ResolutionContext context)
    {
        return source.CatalogItems.Count;
    }
}

If I add this map before the CreateMultiLingualMap the CatalogItemName Maps correctly but the ItemCount fails. If I put the Projection or Resolver after the CreateMultiLingualMap then the ItemCount maps correctly but the CatalogItemName fails to map.

How do I create a Map Profile to allow for both of the properties to be mapped?

Thank you.


Solution

  • For your case, you don't need to use the extension CreateMultiLingualMap. You can create your own mapping definition with multilingual mapping included.

    That's how it is done;

    configuration.CreateMap<CategoryItem, ReadCategoryItemFilterDto>()
        .BeforeMap((source, destination, context) =>
        {
            var translation = source.Translations.FirstOrDefault(pt => pt.Language == CultureInfo.CurrentUICulture.Name);
            if (translation != null)
            {
                context.Mapper.Map(translation, destination);
                return;
            }
    
    
            var multiLingualMapContext = new MultiLingualMapContext(IocManager.Instance.Resolve<ISettingManager>());
            var defaultLanguage = multiLingualMapContext.SettingManager.GetSettingValue(LocalizationSettingNames.DefaultLanguage);
    
            translation = source.Translations.FirstOrDefault(pt => pt.Language == defaultLanguage);
    
            if (translation != null)
            {
                context.Mapper.Map(translation, destination);
                return;
            }
    
            translation = source.Translations.FirstOrDefault();
            if (translation != null)
            {
                context.Mapper.Map(translation, destination);
            }
    
        }).ForMember(dest => dest.ItemCount, opts => opts.MapFrom(src => src.CatalogItems.Count));