我正在从 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;
}
}