Auto Mapper将字符串转换为整数

19

我正在创建一个简单的MVC4应用程序。

我有一个自动映射器。

 Mapper.CreateMap<SourceClass, DestinationClass>()
      .ForMember(dest => dest.IntphoneNo, 
                  opt => opt.MapFrom(src => src.Stringphoneno));

IntphoneNoPerson 类中的变量,数据类型为int。源属性 Stringphoneno 的数据类型是string

当我进行映射时,出现以下错误:

AutoMapper.dll 中发生了 'AutoMapper.AutoMapperMappingException' 类型的异常,但未在用户代码中处理

但是,如果我将 IntphoneNo 的数据类型从int更改为string,则我的程序可以成功运行。

不幸的是,在我的模型中无法更改数据类型。

是否有任何方法可以在映射中更改数据类型?类似于以下内容:

.ForMember(dest => dest.IntphoneNo, 
                  opt => opt.MapFrom(src => src.Int32(Stringphoneno));

经过一些研究,我又向前迈进了一步...
如果我的StringPhoneNo是= 123456
那么以下代码将起作用。 我不需要将其解析为字符串

Mapper.CreateMap<SourceClass, DestinationClass>()
      .ForMember(dest => dest.IntphoneNo, 
                  opt => opt.MapFrom(src => src.Stringphoneno));

但当我的StringPhoneNo为12 3456(12后面有一个空格)时,我的代码就无法工作。

有没有办法在automapper中去除Stringphoneno中的空格(Stringphoneno是我从webservice获取的)。

类似以下方式:

Mapper.CreateMap<SourceClass, DestinationClass>()
      .ForMember(dest => dest.IntphoneNo, 
                  opt => opt.MapFrom(src => src.Trim(Stringphoneno))); 

1
我不建议将电话号码(我猜)存储为整数,因为你会丢失国际/区域零前缀数字。此外,有些号码可能非常长。(除非你使用int64)有11个数字时,你会遇到一些溢出问题。 - Jeroen van Langen
@JeroenvanLangen 我曾经听到过类似的建议,有人指出我们不应该使用money SQL类型来存储货币,而应该使用decimal。当我指出我们使用美元工作,并且如果我们有一笔订单的金额实际上大于money类型,我会手动处理订单时,我笑了起来,因为我们是一家小型葡萄酒公司。这并不是很相关,只是一个我一直喜欢的故事。 - Yuriy Faktorovich
@Yuriy Faktorovich - 但是找到一个超过10位数的国际电话号码比得分高于最大货币类型的订单容易得多 :) - Jeroen van Langen
数据源的数据类型不在我的控制范围内。我是通过 Web 服务获取数据源的。 - user2739679
@JeroenvanLangen 我同意,这只是一个我一直喜欢的故事。 - Yuriy Faktorovich
4个回答

25
Mapper.CreateMap<SourceClass, DestinationClass>() 
    .ForMember(dest => dest.IntphoneNo, 
        opt => opt.MapFrom(src => int.Parse(src.Stringphoneno)));

这里有一些使用所描述的映射函数的工作示例代码。

class SourceClass
{
    public string Stringphoneno { get; set; }
}

class DestinationClass
{
    public int IntphoneNo { get; set; }
}

var source = new SourceClass {Stringphoneno = "8675309"};
var destination = Mapper.Map<SourceClass, DestinationClass>(source);

Console.WriteLine(destination.IntphoneNo); //8675309

1
你需要捕获异常,可能是由于字符串到整数的转换不可行。 - ZaoTaoBao
1
捕获多个异常!也许字符串不是一个数字... - ZaoTaoBao
@user2739679 高亮显示 int.Parse(src.Stringphoneno),右键单击它,选择插入断点。 - Yuriy Faktorovich
src.stringphoneNo 的值是 12345678...,但即使我将 IntphoneNo 的数据类型更改为 stringint.Parse 仍然无法正常工作... - user2739679
@user2739679,你需要具体说明那个数字是什么,否则你的问题可能出在其他地方。我已经发布了一些可行的代码。 - Yuriy Faktorovich
显示剩余5条评论

12
您可能会遇到的问题是无法解析字符串,一种解决方法是使用 ResolveUsing:
Mapper.CreateMap<SourceClass, DestinationClass>() 
.ForMember(dest => dest.intphoneno, opts => opts.ResolveUsing(src =>
                double.TryParse(src.strphoneno, out var phoneNo) ? phoneNo : default(double)));

1
这可以是一行代码(...src => double.TryParse(src.strphoneno, out var phoneNo) ? phoneNo : default(double) - Dinerdo
6
这个解决方案似乎不再起作用了。 ResolveUsing 已被移除,在文档中推荐的替代方法 MapFrom 使用表达式树,因此您不能使用 out 参数。 - Guttsy

3

Yuriy Faktorovich的解决方案可推广到所有需要将string转换为int的情况,该方法是通过为IMappingExpression定义扩展方法并使用自定义属性来实现的:

[AttributeUsage(AttributeTargets.Property)]
public class StringToIntConvertAttribute : Attribute
{
}

// tries to convert string property value to integer
private static int GetIntFromString<TSource>(TSource src, string propertyName)
{
    var propInfo = typeof(TSource).GetProperty(propertyName);
    var reference = (propInfo.GetValue(src) as string);
    if (reference == null)
        throw new MappingException($"Failed to map type {typeof(TSource).Name} because {propertyName} is not a string);

    // TryParse would be better
    var intVal = int.Parse(reference);
    return intVal;
}

public static IMappingExpression<TSource, TDestination> StringToIntMapping<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
    var srcType = typeof(TSource);
    foreach (var property in srcType.GetProperties().Where(p => p.GetCustomAttributes(typeof(StringToIntConvertAttribute), inherit: false).Length > 0))
    {
        expression.ForMember(property.Name, opt => opt.MapFrom(src => GetIntFromString(src, property.Name)));
    }

    return expression;
}

为了使此功能起作用,必须执行以下步骤:
  1. Mapping between source and destination must append the mapping extension. E.g.:

    var config = new MapperConfiguration(cfg =>
    {
       // other mappings
    
       cfg.CreateMap<SrcType, DestType>().StringToIntMapping();
    });
    
  2. Apply the attribute to all source string properties that must automatically be converted to integer values


1
这里有一个更简单但不够可扩展的方法。每次调用Mapper.Initialize时,请记得调用.ToInt(),否则您将收到运行时错误。
public class NumberTest
{
    public static void Main(string[] args)
    {
        Console.WriteLine("".ToInt());
        // 0
        Console.WriteLine("99".ToInt());
        // 99
    }
}

public static class StringExtensions
{
    public static int ToInt(this string text)
    {
        int num = 0;
        int.TryParse(text, out num);
        return num;
    }
}

// Use like so with Automapper
.ForMember(dest => dest.WidgetID, opts => opts.MapFrom(src => src.WidgetID.ToInt()));

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接