Я использую отличную библиотеку CsvHelper (в настоящее время v12.2.2) для создания файла CSV и пытаюсь добавить свои собственные атрибуты, чтобы указать особое форматирование непосредственно в классе.
Запись, которую я пишу, выглядит так (хотя с ~ 200 числовыми полями, как того требует интеграция):
class PayrollRecord {
public int EmployeeID { get; set; }
public decimal RegularPay { get; set; }
public decimal RegularHours { get; set; }
public decimal RegularRate { get; set; }
public decimal OvertimePay { get; set; }
public decimal OvertimeHours { get; set; }
public decimal OvertimeRate { get; set; }
// many many more
}
и мне нужно убедиться, что Pay написано с двумя десятичными знаками, часы - с 3, а ставка выплаты - с 4; этого требует интеграция.
Что работает сейчас
Я создал десятичный преобразователь, который прикрепляю к карте классов:
using CsvHelper;
using CsvHelper.TypeConversion;
// convert decimal to the given number of places, and zeros are
// emitted as blank.
public abstract class MyDecimalConverter : DefaultTypeConverter
{
protected virtual string getFormat() => "";
public override string ConvertToString(object value, IWriterRow row, MemberMapData memberMapData)
{
if (value is decimal d)
return (d == 0) ? string.Empty : string.Format(getFormat(), d);
return base.ConvertToString(value, row, memberMapData);
}
}
public class DollarsConverter : MyDecimalConverter
{
protected override string getFormat() => "{0:0.00}"; // 2 decimal places
}
public class HoursConverter : MyDecimalConverter
{
protected override string getFormat() => "{0:0.000}"; // 3 decimal places
}
public class PayRateConverter : MyDecimalConverter
{
protected override string getFormat() => "{0:0.0000}"; // 4 decimal places
}
а затем я применяю их, когда создаю писателя:
CsvWriter Writer = new CsvWriter( /* stuff */ );
var classMap = new DefaultClassMap<PayrollRecord>();
classMap.AutoMap();
classMap.Map(m => m.RegularPay).TypeConverter<DollarsConverter>();
classMap.Map(m => m.RegularHours).TypeConverter<HoursConverter>();
classMap.Map(m => m.RegularRate).TypeConverter<PayRateConverter>();
classMap.Map(m => m.OvertimePay).TypeConverter<DollarsConverter>();
classMap.Map(m => m.OvertimeHours).TypeConverter<HoursConverter>();
classMap.Map(m => m.OvertimeRate).TypeConverter<PayRateConverter>();
// many more
Writer.Configuration.RegisterClassMap(classMap);
...
Это делает все правильно, но плохо масштабируется: с ~ 200 полями будет сложно поддерживать синхронизацию сопоставления с фактическими определениями полей, и я очень ожидаю, что запись структуру, которую нужно изменить, пока мы не добьемся интеграции.
Боковое примечание: можно аннотировать каждое поле атрибутом [Format("..")]
, но чтобы получить искомое подавление нуля, строка формата представляет собой уродливую вещь из трех частей, которая выглядит очень легко ошибиться и очень утомительно для изменения.
Что бы я хотел
Я хотел бы создать свои собственные настраиваемые атрибуты, которые я могу применить к каждому члену поля, чтобы указать это, чтобы это выглядело примерно так:
// custom attribute
public enum NumericType { Dollars, Hours, PayRate };
public class DecimalFormatAttribute : System.Attribute
{
public NumericType Type { get; }
public DecimalFormatAttribute(NumericType t) => Type = t;
}
// then later
class PayrollRecord {
[DecimalFormat(NumericType.Dollars)] public decimal RegularPay { get; set; }
[DecimalFormat(NumericType.Hours)] public decimal RegularHours { get; set; }
[DecimalFormat(NumericType.PayRate)] public decimal RegularRate { get; set; }
// etc.
}
Я застрял в том, как приклеить свой настраиваемый атрибут к карте классов, и я думаю, что код будет выглядеть примерно так:
var classMap = new DefaultClassMap<PayrollRecord>();
classMap.AutoMap();
foreach (var prop in typeof(PayrollRecord).GetProperties())
{
var myattr = (DecimalFormatAttribute)prop.GetCustomAttribute(typeof(DecimalFormatAttribute));
if (myattr != null)
{
// prop.Name is the base name of the field
// WHAT GOES HERE?
}
}
Я ковылял с этим несколько часов и не могу найти, как это сделать.
CsvHelper.Configuration.Attributes.TypeConverterAttribute
к вашей модели? См. dotnetfiddle.net/m2nORw. - person dbc   schedule 23.12.2019[Name("Regular Pay")]
атрибутов. - person Steve Friedl   schedule 23.12.2019