Применить настраиваемый преобразователь CsvHelper ко всем строковым свойствам набора классов

Я использую отличную библиотеку CsvHelper Джоша Клоуз для чтения файлов csv и загрузки их в базу данных с помощью entity framework. Все это работает хорошо, за исключением одного; CsvReader хранит пустую строку в файле csv как пустую строку в базе данных, и я бы хотел, чтобы вместо этого было значение NULL. Итак, я создал специальный конвертер, который позаботится об этом:

public class NullStringConverter : StringConverter
{
    public override object ConvertFromString(TypeConverterOptions options, string text)
    {
        if (string.IsNullOrEmpty(text))
            return null;
        else
            return base.ConvertFromString(options, text);
    }
}

Я могу применить это к строковому свойству, используя либо плавный синтаксис карты, либо через атрибут, и теперь он будет вставлять NULL вместо пустой строки.

Поскольку у меня довольно много классов, содержащих ряд строковых атрибутов, я бы не хотел создавать операторы Map для каждого из них. Я создал общий класс Map, который перечисляет все свойства и применяет настраиваемый преобразователь ко всем свойствам строки. Вот что у меня есть

public class DefaultStringMap<TEntity> : CsvClassMap<TEntity> where TEntity : AbstractAmtSourceEntity
{
    public DefaultStringMap()
    {
        typeof(TEntity).GetProperties()
            .Where(p => p.PropertyType == typeof(string))
            .ToList()
            .ForEach(p => Map(m => p.Name).TypeConverter<NullStringConverter>());
    }
}

Здесь AbstractAmtSourceEntity - мой базовый класс для всех моих классов сущностей. Вот мой класс читателя, который действительно получает данные:

public static void Read<TEntity>(TextReader reader, AmtSourceModel context) where TEntity : AbstractAmtSourceEntity { using (var csvReader = new CsvReader(reader)) { csvReader.Configuration.WillThrowOnMissingField = false; csvReader.Configuration.Delimiter = "|"; csvReader.Configuration.SkipEmptyRecords = true; csvReader.Configuration.RegisterClassMap<DefaultStringMap<Entity1>>(); csvReader.Configuration.RegisterClassMap<DefaultStringMap<Entity2>>(); etc... csvReader.Configuration.IgnoreReadingExceptions = true; csvReader.Configuration.ReadingExceptionCallback = (ex, row) => { _log.Warn($"Exception caught reading row {row}", ex); _log.Debug($"Exception detail: {ex.Data["CsvHelper"]}"); }; var records = csvReader.GetRecords<TEntity>(); context.Set<TEntity>().AddRange(records); context.SaveChanges(); } } Однако это не работает, сопоставление не применяется, поэтому, очевидно, я чего-то упускаю. Кто-нибудь может сказать мне, чего здесь не хватает?


person Hintham    schedule 09.09.2016    source источник


Ответы (1)


Вы можете настроить конвертеры глобально.

TypeConverterFactory.AddConverter( typeof( string ), new NullStringConverter() );
// or
TypeConverterFactory.AddConverter<string>( new NullStringConverter() );
person Josh Close    schedule 10.09.2016
comment
Супер, вот что я искал. - person Hintham; 12.09.2016
comment
Глобальное переопределение StringConverter у меня не работает в последней версии (2.16.3). Работает нормально, если я укажу конвертер явно, как указано в вопросе. - person Codure; 20.06.2017
comment
Обратите внимание, что начиная с версии 3.0 TypeConverterFactory теперь называется TypeConverterCache и доступен из вашего экземпляра CsvReader, например. reader.Configuration.TypeConverterCache. Ссылка - person Rossco; 19.11.2018