Автоматическое преобразование Mapper из строки в Int

Я создаю простое приложение MVC4

У меня есть автомаппер

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

IntphoneNo имеет тип данных int ( IntphoneNo является переменной моего класса Person) Исходный атрибут Stringphoneno имеет тип данных string .

Когда я сопоставляю, я получаю следующую ошибку.

Исключение типа «AutoMapper.AutoMapperMappingException» возникло в AutoMapper.dll, но не было обработано в пользовательском коде.

Но когда я меняю тип данных IntphoneNo с int на string, моя программа работает успешно.

К сожалению, я не могу изменить тип данных в моей модели.

Есть ли способ изменить Datatupe в отображении. Что-то вроде ниже

.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), мой код не работает. Есть ли способ обрезать пробелы в Stringphoneno (Stringphoneno, который я получаю от веб-сервиса) в automapper.

Что-то вроде ниже..

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

person user2739679    schedule 12.09.2013    source источник
comment
Я бы не советовал хранить номера телефонов (я полагаю) в виде целых чисел, вы потеряете международные/региональные номера с нулевым префиксом. Кроме того, некоторые числа могут быть очень длинными. (если вы не используете int64) С 11 числами у вас возникли проблемы с переполнением.   -  person Jeroen van Langen    schedule 12.09.2013
comment
@JeroenvanLangen В какой-то момент я наткнулся на такой же совет, когда кто-то указал, что мы не должны использовать тип денег sql для хранения денег, а вместо этого должны использовать десятичное число. Я хорошо посмеялся, когда указал, что мы работаем в долларах, и если бы у нас когда-нибудь был заказ, в котором покупка была бы больше, чем денежный тип, я бы сделал заказ вручную, мы были небольшой винной компанией. Не совсем актуально, просто история, которая мне всегда нравилась.   -  person Yuriy Faktorovich    schedule 12.09.2013
comment
@Yuriy Faktorovich - Но найти международный номер телефона, состоящий более чем из 10 цифр, намного проще, чем получить заказ, превышающий максимальный размер денег :)   -  person Jeroen van Langen    schedule 12.09.2013
comment
Тип источника данных не находится под моим контролем. Я получаю источник через веб-сервис.   -  person user2739679    schedule 12.09.2013
comment
@JeroenvanLangen Согласен, просто история, которая мне всегда нравилась.   -  person Yuriy Faktorovich    schedule 12.09.2013


Ответы (4)


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
person Yuriy Faktorovich    schedule 12.09.2013
comment
вам нужно зафиксировать исключение, возможно, приведение к строке --> int невозможно. - person ZaoTaoBao; 12.09.2013
comment
stackoverflow.com/questions/136035/ для поймать исключение!! может строка не число... - person ZaoTaoBao; 12.09.2013
comment
@user2739679 user2739679 int.Parse и Int32.Parse буквально одно и то же, int сопоставляется с Int32. Вы можете поместить точку останова в лямбда-выражение, которое выполняет синтаксический анализ, чтобы увидеть, какое значение имеет Stringphoneno, чтобы узнать, нужно ли вам отформатировать его, например, сначала удалить тире. - person Yuriy Faktorovich; 12.09.2013
comment
я не могу поставить точку останова на строке внутри Mapper.CreateMap. Когда я пытаюсь поставить точку останова, она выбирает всю область автомаппера. - person user2739679; 12.09.2013
comment
@user2739679 user2739679 выделите int.Parse(src.Stringphoneno), щелкните его правой кнопкой мыши, выберите вставить точку останова. - person Yuriy Faktorovich; 12.09.2013
comment
значение src.stringphoneNo равно 12345678... но int.Parse не работает, даже если я изменю тип данных IntphoneNo на строка... - person user2739679; 12.09.2013
comment
@user2739679 user2739679 вам нужно будет конкретно указать этот номер, иначе ваша проблема в другом. Я разместил некоторый рабочий код. - person Yuriy Faktorovich; 12.09.2013
comment
@ Юрий Факторович.. StringPhoneNo я получаю от веб-службы.. Не имею никакого контроля над StringPhoneNo.. Единственное, что я вижу, это DATATYPE для StringPhoneNo является строкой, а DATATYPE для IntPhoneNo в моей модели является строкой. - person user2739679; 13.09.2013
comment
@ Юрий Факторович.. если быть точным, значение в **StringPhoneNo** равно 12 23456 ..(после 12 есть пробел)... - person user2739679; 13.09.2013
comment
@user2739679 user2739679, если это ваша модель, вам следует изменить ее на строку. Также вы можете просто заменить это пространство перед int.Parse. - person Yuriy Faktorovich; 13.09.2013

Проблема, с которой вы можете столкнуться, заключается в том, что когда он не может проанализировать строку, одним из вариантов будет использование ResolveUsing:

Mapper.CreateMap<SourceClass, DestinationClass>() 
.ForMember(dest => dest.intphoneno, opts => opts.ResolveUsing(src =>
                double.TryParse(src.strphoneno, out var phoneNo) ? phoneNo : default(double)));
person StuckOverflow    schedule 26.02.2017
comment
это может быть однострочник (...src => double.TryParse(src.strphoneno, out var phoneNo) ? phoneNo : default(double)) - person Dinerdo; 15.12.2018
comment
Это решение, похоже, больше не работает. ResolveUsing был удален, а рекомендуемая в документации альтернатива MapFrom использует деревья выражений, поэтому вы не можете использовать аргументы out. - person Guttsy; 11.02.2021

Решение 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. Сопоставление между источником и местом назначения должно сопровождаться расширением сопоставления. Например.:

    var config = new MapperConfiguration(cfg =>
    {
       // other mappings
    
       cfg.CreateMap<SrcType, DestType>().StringToIntMapping();
    });
    
  2. Примените атрибут ко всем свойствам исходной строки, которые должны автоматически преобразовываться в целочисленные значения.

person Alexei - check Codidact    schedule 08.12.2016

Вот более простой, но менее масштабируемый подход. Всякий раз, когда вы вызываете 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()));
person Mitch Stewart    schedule 19.04.2017