How to make AutoMapper truncate strings according to MaxLength attribute?

老子叫甜甜 提交于 2020-01-03 13:16:23

问题


I have a DTO I want to map to an entity. The entity has some properties decorated with the MaxLength attribute.

I would like AutoMapper to truncate all the strings coming from the DTO when mapping to my entity according to the MaxLength for each property, so that I don't get validation errors when saving the entity.

So, if entity is defined like this:

public class Entity 
{
    [MaxLength(10)]
    string Name { get; set; }
}

I would like that doing this:

var myDto = new MyDto() { Name = "1231321312312312312312" };
var entity = Mapper.Map<Entity>(myDto);

The resulting entity should have its Name limited to a maximum of 10 characters.


回答1:


I'm not sure that it's a good place to put that logic, but here is an example that should work in your case (AutoMapper 4.x): Custom Mapping with AutoMapper

In this example, I'm reading a custom MapTo property on my entity, you could do the same with MaxLength.

Here a full example with the current version of AutoMapper (6.x)

class Program
{
    static void Main(string[] args)
    {
        Mapper.Initialize(configuration =>
            configuration.CreateMap<Dto, Entity>()
                .ForMember(x => x.Name, e => e.ResolveUsing((dto, entity, value, context) =>
                {
                    var result = entity.GetType().GetProperty(nameof(Entity.Name)).GetCustomAttribute<MaxLengthAttribute>();
                    return dto.MyName.Substring(0, result.Length);
                })));

        var myDto = new Dto { MyName = "asadasdfasfdaasfasdfaasfasfd12" };
        var myEntity = Mapper.Map<Dto, Entity>(myDto);
    }
}

public class Entity
{
    [MaxLength(10)]
    public string Name { get; set; }
}

public class Dto
{
    public string MyName { get; set; }
}



回答2:


For AutoMapper 8.0, and building on @SuperJMN's answer:

Create a file AutoMapperExtensions.cs in your project:

using AutoMapper;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;

namespace YourNamespaceHere
{
    public static class AutoMapperExtensions
    {
        public static IMappingExpression<TSource, TDestination> LimitStrings<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
        {
            var sourceType = typeof(TSource);
            var destinationType = typeof(TDestination);

            var existingMaps = Mapper.Configuration.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);

            var propertyMaps = existingMaps.PropertyMaps.Where(map => !map.Ignored && ((PropertyInfo)map.SourceMember).PropertyType == typeof(string));

            foreach (var propertyMap in propertyMaps)
            {
                var attr = propertyMap.DestinationMember.GetCustomAttribute<MaxLengthAttribute>();
                if (attr != null)
                {
                    expression.ForMember(propertyMap.DestinationMember.Name,
                        opt => opt.ConvertUsing(new StringLimiter(attr.Length), propertyMap.SourceMember.Name));
                }
            }

            return expression;
        }
    }

    public class StringLimiter : IValueConverter<string, string>
    {
        private readonly int length;
        private readonly PropertyInfo propertyMapSourceMember;

        public StringLimiter(int length)
        {
            this.length = length;
            propertyMapSourceMember = propertyMapSourceMember ?? throw new ArgumentNullException(nameof(propertyMapSourceMember));
        }

        public string Convert(string sourceMember, ResolutionContext context)
        {
            var sourceValue = (string)propertyMapSourceMember.GetValue(sourceMember);
            return new string(sourceValue.Take(length).ToArray());
        }
    }
}

... and add the following to the end of your CreateMap (e.g.):

.ForMember(
    dest => dest.ShortField,
    opts => opts.MapFrom(src => src.LongField))
.LimitStrings();



回答3:


For Automapper 4.x I got something with this code:

class Program
{
    static void Main(string[] args)
    {
        Mapper.Initialize(configuration =>
            configuration.CreateMap<Dto, Entity>()
                .ForMember(x => x.Name, e => e.MapFrom(d => d.MyName))
                .ForMember(x => x.Free, e => e.MapFrom(d => d.Free))
                .ForMember(x => x.AnotherName, e => e.MapFrom(d => d.Another))
                .LimitStrings());

        var dto = new Dto() { MyName = "asadasdfasfdaasfasdfaasfasfd12", Free = "ASFÑLASJDFÑALSKDJFÑALSKDJFAMLSDFASDFASFDASFD", Another = "blalbalblalblablalblablalblablalblablabb"};
        var entity = Mapper.Map<Entity>(dto);
    }
}

public static class Extensions
{
    public static IMappingExpression<TSource, TDestination> LimitStrings<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
    {
        var sourceType = typeof(TSource);
        var destinationType = typeof(TDestination);

        var existingMaps = Mapper.GetAllTypeMaps().First(b => b.SourceType == sourceType && b.DestinationType == destinationType);

        var propertyMaps = existingMaps.GetPropertyMaps().Where(map => !map.IsIgnored() && ((PropertyInfo)map.SourceMember).PropertyType == typeof(string));

        foreach (var propertyMap in propertyMaps)
        {
            var attr = propertyMap.DestinationProperty.MemberInfo.GetCustomAttribute<MaxLengthAttribute>();
            if (attr != null)
            {
                expression.ForMember(propertyMap.DestinationProperty.Name,
                    opt => opt.ResolveUsing(new StringLimiter(attr.Length, (PropertyInfo) propertyMap.SourceMember)));
            }                                            
        }

        return expression;
    }
}

public class StringLimiter : IValueResolver
{
    private readonly int length;
    private readonly PropertyInfo propertyMapSourceMember;

    public StringLimiter(int length, PropertyInfo propertyMapSourceMember)
    {
        this.length = length;
        this.propertyMapSourceMember = propertyMapSourceMember ?? throw new ArgumentNullException(nameof(propertyMapSourceMember));
    }

    public ResolutionResult Resolve(ResolutionResult source)
    {
        var sourceValue = (string)propertyMapSourceMember.GetValue(source.Context.SourceValue);
        var result = new string(sourceValue.Take(length).ToArray());
        return source.New(result);
    }
}

Please, tell me if it has sense or has some bugs!

Thanks to @bidou for the tip. Here is the post where I took the inspiration here



来源:https://stackoverflow.com/questions/49799389/how-to-make-automapper-truncate-strings-according-to-maxlength-attribute

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!