如何使AutoMapper根据MaxLength属性截断字符串?

时间:2018-04-12 14:37:40

标签: c# .net automapper

我有一个我要映射到实体的DTO。该实体具有一些使用MaxLength属性修饰的属性。

我希望AutoMapper在根据每个属性的MaxLength映射到我的实体时截断来自DTO的所有字符串,以便在保存实体时不会出现验证错误。

因此,如果实体的定义如下:

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

我希望这样做:

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

结果entity应将Name限制为最多10个字符。

3 个答案:

答案 0 :(得分:2)

我不确定这是一个放置逻辑的好地方,但这是一个应该适用于您的情况的示例(AutoMapper 4.x):Custom Mapping with AutoMapper

在此示例中,我在我的实体上阅读自定义MapTo属性,您可以对MaxLength执行相同操作。

这是使用当前版本的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; }
}

答案 1 :(得分:0)

我得到了一些代码:

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);
    }
}

请告诉我它是否有意义或有一些错误!

感谢@bidou提示。这是我获取灵感的帖子here

答案 2 :(得分:0)

对于AutoMapper 8.0,并基于@SuperJMN的答案:

在您的项目中创建文件AutoMapperExtensions.cs:

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());
        }
    }
}

...,然后将以下内容添加到CreateMap的末尾(例如):

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