0

我正在从 JObject 映射到自定义类,它工作正常,但我宁愿告诉它一次执行这种默认类型的映射,而不是必须为每个属性执行一个 .MapFrom。大多数源“属性”只是目标上 Pascal 大小写属性名称的小写下划线版本。

由于 JObject 没有我想要的值作为属性,我必须通过 MapFrom 对它们进行索引(我不能使用 SourceMemberNamingConvention/DestinationMemberNamingConvention)。所以我想要一个表达式,我可以通过 ForAllOtherMembers 之类的东西来应用从 JObject 检索这个默认值,但我不能让它工作......我有什么选择?

我曾尝试使用 ResolveUsing 方法,但我也必须使用 ConstructedBy 方法,但 ResolveUsing 返回一个 void(现在?在版本 5 中),所以我无法对其执行 .ConstructedBy() 。

下面我必须执行每个 MapFrom,即使它们遵循相同的访问模式:

   MapperConfiguration MapperConfig = new MapperConfiguration(cfg => {
   cfg.CreateMap<string, bool>().ConvertUsing<BooleanTypeConverter>();
   cfg.CreateMap<JObject, FiscalYear>()
       .ForMember("EmployeeName", 
            options => options.MapFrom(jo => jo["employee_name"]))
       .ForMember("YearName", 
            options => options.MapFrom(jo => jo["year_name"]));

更新:我继续做更多的手动方式并用委托替换字符串

public static class AutoMapperConfig
{
    public static MapperConfiguration MapperConfig = new MapperConfiguration(cfg => {
        cfg.CreateMap<string, bool>().ConvertUsing<BooleanTypeConverter>();

        cfg.CreateMap<JToken, FiscalYear>()
            .Map(d => d.EmployeeName)
            .Map(d => d.Year, "int_year")
            .Map(d => d.Name, "year");
    });
}

public static class MappingExpressionExtensions
{
    private static readonly MatchEvaluator ToSnakeCaseEvaluator = m =>
    {
        string match = m.ToString();
        return "_" + char.ToLower(match[0]) + match.Substring(1);
    };

    public static IMappingExpression<JToken, TDest> Map<TDest, TDestProp>(this IMappingExpression<JToken, TDest> map,
        Expression<Func<TDest, TDestProp>> propertyExpression, string sourceName = null)
    {
        var propertyName = propertyExpression.PropertyName();
        sourceName = string.IsNullOrWhiteSpace(sourceName)
                    ? new Regex("([A-Z])").Replace(propertyName, ToSnakeCaseEvaluator).TrimStart('_')
                    : sourceName;
        var propType = typeof(TDestProp);
        if (propType == typeof(bool))
        {
            // in order for BooleanTypeConverter to work, convert to string and run as a normal mapping
            map.ForMember(propertyExpression.PropertyName(), o => o.MapFrom(jo => jo[sourceName].ToString()));
            return map;
        }

        var isNullableGenericType = propType.IsGenericType && propType.GetGenericTypeDefinition() == typeof(Nullable<>);
        var underlyingType = isNullableGenericType ? Nullable.GetUnderlyingType(propType) : propType;
        TypeConverter converter = TypeDescriptor.GetConverter(underlyingType);

        map.ForMember(propertyName,
            o => o.MapFrom(jo => (isNullableGenericType && string.IsNullOrWhiteSpace(jo[sourceName].ToString()))
                    || (IsNumeric(jo[sourceName]) && string.IsNullOrWhiteSpace(jo[sourceName].ToString()))
                        ? 0 : converter.ConvertFrom(jo[sourceName].ToString())));

        return map;
    }

    public static bool IsNumeric(object expression)
    {
        if (expression == null)
            return false;

        double number;
        return Double.TryParse(Convert.ToString(expression, CultureInfo.InvariantCulture),
            NumberStyles.Any, NumberFormatInfo.InvariantInfo, out number);
    }
}

public class BooleanTypeConverter : ITypeConverter<string, bool>
{
    /// <summary>
    /// Automapper version compatible with version 4.x
    /// </summary>
    /// <param name="context"></param>
    /// <returns></returns>
    public bool Convert(ResolutionContext context)
    {
        switch (context.SourceValue.ToString().ToLower().Trim())
        {
            case "true":
            case "yes":
            case "y":
            case "1":
                return true;
        }

        return false;
    }

    //// Automapper version compatible with version 5.0+
    //public bool Convert(string source, bool destination, ResolutionContext context)
    //{
    //    switch (source.ToLower().Trim())
    //    {
    //        case "true":
    //        case "yes":
    //        case "y":
    //        case "1":
    //            return true;
    //    }

    //    return false;
    //}  

// put this in static class
    public static string PropertyName<T, TProperty>(this Expression<Func<T, TProperty>> property)
{
  if (property.Body is MemberExpression)
    return ((MemberExpression) property.Body).Member.Name;
  return ((MemberExpression) ((UnaryExpression) property.Body).Operand).Member.Name;
}
}
4

0 回答 0