Cinchoo ETL - FixedLength 写入器
适用于 .NET 的简单 FixedLength 文件写入器
目录
- 引言
- 要求
- “Hello World!”示例
- 写入所有记录
- 手动写入记录
- 自定义 FixedLength 记录
- 自定义 FixedLength 标头
- 自定义 FixedLength 字段
- 保留
- 回调机制
- 自定义
- 使用动态对象
- 异常
- 技巧
- 使用 MetadataType 注解
- 配置选项
- ToText 辅助方法
- Writing DataReader 辅助方法
- Writing DataTable 辅助方法
- 高级主题 (Advanced Topics)
1. 引言
ChoETL 是一个用于 .NET 的开源 ETL(提取、转换和加载)框架。它是一个基于代码的库,用于从多个来源提取数据、进行转换,并将其加载到您自己的 .NET 环境中的数据仓库中。您可以立即将数据导入您的数据仓库。
本文讨论了 ChoETL 框架提供的 FixedLengthRWriter
组件。它是一个简单的实用程序类,用于将 FixedLength
数据保存到文件中。
注意:相应的 FixedLengthReader 文章可在此处找到:此处。
特点
- 遵循
FixedLength
标准文件规则。优雅地处理包含逗号和换行符的数据字段。 - 除了逗号,还可以使用大多数分隔字符,包括制表符分隔的字段。
- 生成文件时支持特定文化的日期、货币和数字格式。
- 支持不同的字符编码。
- 在写入文件时提供对日期、货币、枚举、布尔值、数字格式的精细控制。
- 详细而健壮的错误处理,让您能够快速查找和修复问题。
- 缩短您的开发时间。
2. 要求
此框架库是用 C# 编写的,使用 .NET 4.5 Framework。
3. “Hello World!” 示例
- 打开 VS.NET 2013 或更高版本
- 创建一个示例 VS.NET (.NET Framework 4.5) 控制台应用程序项目
- 通过 包管理器控制台 使用 Nuget 命令安装 ChoETL:
Install-Package ChoETL
- 使用
ChoETL
命名空间
让我们从一个简单的示例开始,生成具有 2 列的以下 FixedLength
文件。
清单 3.1 示例 FixedLength 数据文件(记录长度:18 个字符)
Id Name
00000001Mark
00000002Jason
有多种方法可以以最少的设置创建 FixedLength
文件。
3.1. 快速写入 - 数据优先方法
这是快速创建 FixedLength
文件的方法。不需要类型化的 POCO 对象。下面的示例代码展示了如何使用动态对象生成上述示例 FixedLength
文件。
清单 3.1.1 将对象列表写入 FixedLength 文件
static void QuickWriteTest()
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter("Emp.txt").
WithField("Id", 0, 8. fieldType: typeof(int)).
WithField("Name", 8, 10))
{
parser.Write(objs);
}
}
在上述示例中,使用流式 API 定义每个列的起始索引和大小,然后我们将对象列表一次性传递给 FixedLengthWriter
,将它们写入 FixedLength
文件。
清单 3.1.2 将每个对象写入 FixedLength 文件
static void QuickWriteTest2()
{
using (var parser = new ChoFixedLengthWriter("Emp.txt").
WithField("Id", 0, 8, fieldType: typeof(int)).
WithField("Name", 8, 10))
{
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
parser.Write(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
parser.Write(rec2);
}
}
在上述示例中,我们控制构造并将每个单独的记录传递给 FixedLengthWriter
,以使用 Write
重载生成 FixedLength
文件。
3.2. 代码优先方法
这是使用类型化 POCO 类生成 FixedLength
文件的另一种方法。首先,定义一个简单的 POCO 类以匹配底层 FixedLength
文件布局。
清单 3.2.1 简单的 POCO 实体类
public partial class EmployeeRecSimple
{
public int Id { get; set; }
public string Name { get; set; }
}
在上面,POCO 类定义了两个属性,匹配示例 FixedLength
文件模板。
清单 3.2.2 保存到 FixedLength 文件
List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();
EmployeeRecSimple rec1 = new EmployeeRecSimple();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRecSimple rec2 = new EmployeeRecSimple();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter<EmployeeRecSimple>("Emp.txt").
WithField("Id", 0, 8, fieldType: typeof(int)).
WithField("Name", 8, 10))
{
parser.Write(objs);
}
上述示例展示了如何从类型化 POCO 类对象创建 FixedLength
文件。
3.3. 配置优先方法
在此模型中,我们定义了 FixedLength
配置以及生成示例 FixedLength
文件所需的所有必要参数和 FixedLength
列。
清单 3.3.1 定义 FixedLength 配置
ChoFixedLengthRecordConfiguration config = new ChoFixedLengthRecordConfiguration();
config.FixedLengthRecordFieldConfigurations.Add
(new ChoFixedLengthRecordFieldConfiguration("Id", 0, 8, fieldType: typeof(int)));
config.FixedLengthRecordFieldConfigurations.Add
(new ChoFixedLengthRecordFieldConfiguration("Name", 8, 10));
在上面,该类定义了两个 FixedLength
属性,匹配示例 FixedLength
文件模板。
清单 3.3.2 无 POCO 对象生成 FixedLength 文件
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Tom";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter("Emp.txt", config))
{
parser.Write(objs);
}
上述示例代码展示了如何使用预定义的 FixedLength
配置设置从动态对象列表生成 FixedLength
文件。在 FixedLengthWriter
构造函数中,我们指定了 FixedLength
配置对象,以便在创建文件时遵循 FixedLength
布局模式。如果 FixedLength
列的名称或数量存在任何不匹配,将报告为错误并停止写入过程。
清单 3.3.3 使用 POCO 对象保存 FixedLength 文件
List<EmployeeRecSimple> objs = new List<EmployeeRecSimple>();
EmployeeRecSimple rec1 = new EmployeeRecSimple();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRecSimple rec2 = new EmployeeRecSimple();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter<EmployeeRecSimple>
("Emp.txt", config))
{
parser.Write(objs);
}
上述示例代码展示了如何使用 FixedLength
配置对象从 POCO 对象列表生成 FixedLength
文件。在 FixedLengthWriter
构造函数中,我们指定了 FixedLength
配置对象。
这是结合定义 POCO 实体类和声明性附加 FixedLength
配置参数的方法。 id
是一个必需列,name
是一个可选值列,默认值为 "XXXX
"。如果 name
不存在,它将取默认值。
清单 3.4.1 定义 POCO 对象
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
[Required]
public int? Id
{
get;
set;
}
[ChoFixedLengthRecordField(8, 10)]
[DefaultValue("XXXX")]
public string Name
{
get;
set;
}
public override string ToString()
{
return "{0}. {1}.".FormatString(Id, Name);
}
}
上面的代码说明了如何使用生成 FixedLength
文件所需的必要属性定义 POCO 对象。首先,为每个记录字段定义属性,并使用 ChoFixedLengthRecordFieldAttribute
来符合 FixedLength
记录映射。每个属性必须指定字段的 StartIndex
和 Size
才能映射到 FixedLength
列。 StartIndex
是从 0
开始的。 Id
是一个必需属性。我们用 RequiredAttribute
装饰它。 Name
使用 DefaultValueAttribute
赋予默认值。这意味着如果对象中未设置 Name
值,FixedLengthWriter
将使用默认值 'XXXX
' 写入文件。
它非常简单,可以立即保存 FixedLength
数据。
清单 3.4.2 使用 POCO 对象保存 FixedLength 文件
List<EmployeeRec> objs = new List<EmployeeRec>();
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter<EmployeeRec>("Emp.txt"))
{
parser.Write(objs);
}
我们首先创建一个 ChoFixedLengthWriter
对象的新实例。就是这样。所有从对象生成 FixedLength
数据的繁重工作都由写入器在底层完成。
默认情况下,FixedLengthWriter
在保存 FixedLength
文件时会发现并使用默认配置参数。这些参数可以根据您的需要进行覆盖。以下部分将为您深入介绍每个配置属性的详细信息。
4. 写入所有记录
它就像设置 POCO 对象以匹配 FixedLength
文件结构、构造对象列表并将其传递给 FixedLengthWriter
的 Write
方法一样简单。这将一次性将整个对象列表写入 FixedLength
文件。
清单 4.1 写入 FixedLength 文件
List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...
using (var parser = new ChoFixedLengthWriter<EmployeeRec>("Emp.txt"))
{
parser.Write(objs);
}
或
清单 4.2 写入 FixedLength 文件流
List<EmployeeRec> objs = new List<EmployeeRec>();
//Construct and attach objects to this list
...
using (var tx = File.OpenWrite("Emp.txt"))
{
using (var parser = new ChoFixedLengthWriter<EmployeeRec>(tx))
{
parser.Write(objs);
}
}
这种模型使您的代码优雅、整洁、易于阅读和维护。
5. 手动写入记录
这是在 POCO 对象以非连接方式构造时,将每个单独的记录写入 FixedLength
文件的替代方法。
清单 5.1 写入 FixedLength 文件
var writer = new ChoFixedLengthWriter<EmployeeRec>("Emp.txt");
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
writer.Write(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec1.Id = 11;
rec1.Name = "Top";
writer.Write(rec2);
6. 自定义 FixedLength 记录
使用 ChoFixedLengthRecordObjectAttribute
,您可以声明性地自定义 POCO 实体对象。
清单 6.1 为每个记录自定义 POCO 对象
[ChoFixedLengthRecordObject(Encoding = "Encoding.UTF32",
ErrorMode = ChoErrorMode.IgnoreAndContinue, IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All)]
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
以下是用于对文件执行 FixedLength
加载操作的可用属性
EOLDelimiter
- 用于分隔FixedLength
行的值。默认值为\r\n
(NewLine)Culture
- 用于读取和写入的文化信息IgnoreEmptyLine
- 不适用Comments
- 不适用QuoteChar
- 用于转义包含分隔符、引号或行尾的字段的值QuoteAllFields
- 一个标志,告诉写入器是否所有写入的字段都应被引号包围;无论该字段是否包含任何需要转义的内容Encoding
-FixedLength
文件的编码HasExcelSeperator
- 不适用ColumnCountStrict
- 此标志指示如果FixedLength
字段配置与数据对象成员不匹配,是否应抛出异常ColumnOrderStrict
- 不适用BufferSize
- 当读取器来自StreamWriter
时使用的内部缓冲区的大小ErrorMode
- 此标志指示在写入过程中是否应抛出异常,并且预期的字段未能写入。可以按属性覆盖。可能的值是IgnoreAndContinue
- 忽略错误,跳过记录并继续处理下一条。ReportAndContinue
- 如果 POCO 实体是IChoNotifyRecordWrite
类型,则向其报告错误ThrowAndStop
- 抛出错误并停止执行
IgnoreFieldValueMode
- 不适用ObjectValidationMode
- 一个标志,告知读取器关于记录对象要执行的验证类型。可能的值是Off
- 不执行对象验证MemberLevel
- 在每个FixedLength
属性写入文件之前执行验证ObjectLevel
- 在所有 POCO 属性写入文件之前执行验证
7. 自定义 FixedLength 标头
通过将 ChoFixedLengthFileHeaderAttribute
声明性地附加到 POCO 实体对象,您可以在创建 FixedLength
文件时影响写入器生成 FixedLength
标头。
清单 6.1 为文件标题自定义 POCO 对象
[ChoFixedLengthFileHeader]
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
以下是您可以根据需要添加一些自定义的可用成员。
FillChar
- 当FixedLength
列标头的大小小于列大小(ChoFixedLengthRecordFieldAttribute.Size
或ChoFixedLengthRecordFieldConfiguration.Size
)时使用的填充字符。默认值为 '\0
',不进行填充Justification
- 列标头对齐方式。默认值为Left
TrimOption
- 不适用Truncate
- 此标志告诉写入器,如果FixedLength
列标头值超出列大小,则截断该值。默认值为false
8. 自定义 FixedLength 字段
对于每个 FixedLength
列,您可以使用 ChoFixedLengthRecordFieldAttribute
在 POCO 实体属性中指定映射。
清单 6.1 自定义 POCO 对象的 FixedLength 列
[ChoFixedLengthFileHeader]
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
以下是您可以为每个属性添加自定义的可用成员
StartIndex
- 列在行中的从零开始的起始字符位置FieldName
-FixedLength
列名标头。如果未指定,将使用 POCO 对象属性名称作为列标头FillChar
- 当FixedLength
列值的大小小于列大小时使用的填充字符。默认值为 '\0
',不进行填充FieldValueJustification
- 列值对齐方式。默认值为Left
FieldValueTrimOption
- 不适用Truncate
- 此标志告诉写入器,如果FixedLength
列值超出列大小,则截断该值。默认值为false
Size
-FixedLength
列值的大小QuoteField
- 一个标志,告诉写入器FixedLength
列值是否用引号包围。ErrorMode
- 此标志指示在写入过程中是否应抛出异常,并且预期的字段未能转换和写入。可能的值是IgnoreAndContinue
- 忽略错误并继续加载记录的其他属性ReportAndContinue
- 如果 POCO 实体是IChoRecord
类型,则向其报告错误ThrowAndStop
- 抛出错误并停止执行
IgnoreFieldValueMode
- 不适用
8.1. DefaultValue
任何 POCO 实体属性都可以使用 System.ComponentModel.DefaultValueAttribute
指定默认值。当 FixedLength
值为 null
时,用于写入的值(通过 IgnoreFieldValueMode
控制)。
8.2. ChoFallbackValue
任何 POCO 实体属性都可以使用 ChoETL.ChoFallbackValueAttribute
指定回退值。当 FixedLength
值无法转换为文本时,该值将用于设置属性。 Fallback
值仅在 ErrorMode
为 IgnoreAndContinue
或 ReportAndContinue
时设置。
8.3. 类型转换器
大多数基本类型会自动转换为 string
/文本并保存到 FixedLength
文件中。如果 FixedLength
字段的值不能自动转换为文本值,您可以指定自定义/内置的 .NET 转换器将值转换为文本。这些转换器可以是 IValueConverter
或 TypeConverter
转换器。
用于将属性值转换为文本的方法是 IValueConverter.ConvertBack()
或 TypeConvert.ConvertTo()
。
清单 8.3.1 指定类型转换器
[ChoFixedLengthFileHeader]
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
public string Name { get; set; }
}
清单 8.3.2 IntConverter 实现
public class IntConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
return value;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
int intValue = (int)value;
return intValue.ToString("D4");
}
}
在上面的示例中,我们定义了自定义 IntConverter
类。并展示了如何用前导零格式化 'Id
' FixedLength
属性。
8.4. 验证
FixedLengthWriter
利用 System.ComponentModel.DataAnnotations
和 Validation Block
验证属性为 POCO 实体的各个字段指定验证规则。请参阅 MSDN 网站以获取可用 DataAnnotations
验证属性的列表。
清单 8.4.1 在 POCO 实体中使用验证属性
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10)]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
}
在上面的示例中,对 Id
属性使用 Range
验证属性。对 Name
属性使用 Required
验证属性。当 Configuration.ObjectValidationMode
设置为 ChoObjectValidationMode.MemberLevel
或 ChoObjectValidationMode.ObjectLevel
时,FixedLengthWriter
在将数据保存到文件之前对它们执行验证。
在某些情况下,您可能希望自己控制并在 POCO 实体类中执行手动 **自验证**。这可以通过继承 POCO 对象实现 IChoValidatable
接口来完成。
清单 8.4.2 对 POCO 实体进行手动验证
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject
(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec : IChoValidatable
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10)]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool TryValidate
(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName, ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
上面的示例展示了如何在 POCO 对象中实现自定义自验证。
IChoValidatable
接口公开以下方法
TryValidate
- 验证整个对象,如果所有验证都通过,则返回true
。否则,返回false
。Validate
- 验证整个对象,如果验证未通过则抛出异常。TryValidateFor
- 验证对象的特定属性,如果所有验证都通过则返回true
。否则,返回false
。ValidateFor
- 验证对象的特定属性,如果验证未通过则抛出异常。
10. 回调机制
FixedLengthWriter
提供开箱即用的行业标准 FixedLength
数据文件生成功能,以满足大多数需求。如果生成过程未能满足您的任何需求,您可以使用 FixedLengthWriter
提供的回调机制来处理此类情况。为了参与回调机制,POCO 实体对象或 DataAnnotation
的 MetadataType
类型对象必须继承 IChoNotifyRecordWrite
接口。
提示:这些接口方法中抛出的任何异常都将被忽略。
IChoNotifyRecordWrite
公开了以下方法
BeginWrite
- 在FixedLength
文件写入开始时调用EndWrite
- 在FixedLength
文件写入结束时调用BeforeRecordWrite
- 在FixedLength
记录写入之前引发AfterRecordWrite
- 在FixedLength
记录写入之后引发RecordWriteError
- 在写入FixedLength
记录时出错时引发BeforeRecordFieldWrite
- 在FixedLength
列值写入之前引发AfterRecordFieldWrite
- 在FixedLength
列值写入之后引发RecordFieldWriteError
- 在写入FixedLength
列值时出错时引发
清单 10.1 直接 POCO 回调机制实现
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject
(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false)]
public partial class EmployeeRec : IChoNotifyrRecordWrite
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
}
清单 10.2 基于 MetaDataType 的回调机制实现
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject
(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.IgnoreAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false)]
public class EmployeeRecMeta : IChoNotifyRecordWrite
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8. 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
}
[MetadataType(typeof(EmployeeRecMeta))]
public partial class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, int.MaxValue, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[Required]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
}
10.1 BeginWrite
此回调在 FixedLength
文件写入的开始时调用一次。 source
是 FixedLength
文件流对象。在这里,您有机会检查 stream
,返回 true
以继续 FixedLength
生成。返回 false
以停止生成。
列表 10.1.1 BeginWrite 回调示例
public bool BeginWrite(object source)
{
StreamReader sr = source as StreamReader;
return true;
}
10.2 EndWrite
此回调在 FixedLength
文件生成结束时调用一次。 source
是 FixedLength
文件流对象。在这里,您有机会检查流,执行对流的任何后续步骤。
列表 10.2.1 EndWrite 回调示例
public void EndWrite(object source)
{
StreamReader sr = source as StreamReader;
}
10.3 BeforeRecordWrite
此回调在每个 POCO 记录对象写入 FixedLength
文件之前调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 source
是 FixedLength
记录行。在这里,您有机会检查 POCO 对象,并在需要时生成 FixedLength
记录行。
提示:如果要跳过写入记录,请将 source 设置为 null。
提示:如果您想控制 FixedLength 记录行的生成,请将 source 设置为有效的 FixedLength 记录行文本。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.3.1 BeforeRecordWrite 回调示例
public bool BeforeRecordWrite(object target, int index, ref object source)
{
source = "1,Raj";
return true;
}
10.4 AfterRecordWrite
此回调在每个 POCO 记录对象写入 FixedLength
文件之后调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 source
是 FixedLength
记录行。在这里,您有机会对记录行执行任何后续操作。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.4.1 AfterRecordWrite 回调示例
public bool AfterRecordWrite(object target, int index, object source)
{
string line = source as string;
return true;
}
10.5 RecordWriteError
此回调在写入 POCO 记录对象时遇到错误时调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 source
是 FixedLength
记录行。 ex
是异常对象。在这里,您有机会处理异常。此方法仅在 Configuration.ErrorMode
为 ReportAndContinue
时调用。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.5.1 RecordWriteError 回调示例
public bool RecordLoadError(object target, int index, object source, Exception ex)
{
string line = source as string;
return true;
}
10.6 BeforeRecordFieldWrite
此回调在每个 FixedLength
记录列写入 FixedLength
文件之前调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 propName
是 FixedLength
记录属性名称。 value
是 FixedLength
列值。在这里,您有机会检查 FixedLength
记录属性值并执行任何自定义验证等。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.6.1 BeforeRecordFieldWrite 回调示例
public bool BeforeRecordFieldWrite(object target, int index, string propName, ref object value)
{
return true;
}
10.7 AfterRecordFieldWrite
此回调在每个 FixedLength
记录列值写入 FixedLength
文件之后调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 propName
是 FixedLength
记录属性名称。 value
是 FixedLength
列值。可以在此处执行任何字段后操作,例如计算其他属性、验证等。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.7.1 AfterRecordFieldWrite 回调示例
public bool AfterRecordFieldWrite(object target, int index, string propName, object value)
{
return true;
}
10.8 RecordWriteFieldError
此回调在写入 FixedLength
记录列值时遇到错误时调用。 target
是 POCO 记录对象的实例。 index
是文件中的行索引。 propName
是 FixedLength
记录属性名称。 value
是 FixedLength
列值。 ex
是异常对象。在这里,您有机会处理异常。此方法仅在 FixedLengthReader
执行以下两个步骤序列后调用
FixedLengthWriter
查找每个FixedLength
属性的FallbackValue
值。如果存在,它会尝试使用它进行写入。- 如果
FallbackValue
值不存在且Configuration.ErrorMode
指定为ReportAndContinue
,则将执行此回调。
返回 true
继续加载过程,否则返回 false
停止过程。
清单 10.8.1 RecordFieldWriteError 回调示例
public bool RecordFieldWriteError(object target, int index, string propName, object value, Exception ex)
{
return true;
}
11. 自定义
FixedLengthWriter
自动检测并从 POCO 实体加载配置设置。在运行时,您可以在 FixedLength
生成之前自定义和调整这些参数。 FixedLengthWriter
暴露 Configuration
属性,它是 ChoFixedLengthRecordConfiguration
对象。使用此属性,您可以执行自定义。
清单 11.1 运行时自定义 FixedLengthWriter
class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter("Emp.txt"))
{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
12. 使用动态对象
到目前为止,本文解释了如何将 FixedLengthWriter
与 POCO 对象一起使用。 FixedLengthWriter
还支持在没有 POCO 实体对象的情况下生成 FixedLength
文件。它利用 .NET 动态特性。下面的示例展示了如何使用动态对象生成 FixedLength
流。 FixedLength
模式是从第一个对象确定的。如果在动态对象成员值中发现不匹配,将引发错误并停止生成过程。
下面的示例展示了这一点
清单 12.1 从动态对象生成 FixedLength 文件
class Program
{
static void Main(string[] args)
{
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 1;
rec1.Name = "Mark";
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 2;
rec2.Name = "Jason";
objs.Add(rec2);
using (var parser = new ChoFixedLengthWriter("Emp.txt"))
{
parser.Configuration.ColumnCountStrict = true;
parser.Write(objs);
}
}
}
13. 异常
FixedLengthReader
在不同情况下抛出不同类型的异常
ChoParserException
-FixedLength
文件损坏,解析器无法恢复ChoRecordConfigurationException
- 指定了任何无效配置设置,将引发此异常ChoMissingRecordFieldException
-FixedLength
列缺少属性,将引发此异常
14. 提示
如果 POCO 对象属性包含换行符/单引号/字段分隔符,则必须将 QuoteField
指定为 true
以优雅地处理这种情况。否则,将引发错误并停止处理。
清单 14.1.1 FixedLength 文件中的多行列值
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject]
public class EmployeeRec
{
[ChoFixedLengthRecordField(0, 8)]
[Required]
[ChoFallbackValue(100)]
[Range(100, 10000)]
public int? Id
{
get;
set;
}
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[DefaultValue("XXXX")]
public string Name
{
get;
set;
}
public override string ToString()
{
return "{0}. {1}.".FormatString(Id, Name);
}
}
15. 使用 MetadataType 注解
Cinchoo ETL 更好地与数据注解的 MetadataType
模型配合使用。它是一种将 MetaData
类附加到数据模型类的方法。在此关联类中,您提供了数据模型中没有的额外元数据信息。其作用是为类添加属性,而无需修改该类。您可以向一个类添加一个接受单个参数的属性,该类将具有所有属性。当 POCO 类由自动工具(通过 Entity Framework、MVC 等)自动生成时,这很有用。这就是第二个类发挥作用的原因。您可以在不触及生成的文件的情况下添加新内容。此外,这通过将关注点分离到多个类中来促进模块化。
有关更多信息,请搜索 MSDN。
清单 15.1 MetadataType 注释用法示例
[MetadataType(typeof(EmployeeRecMeta))]
public class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
[ChoFixedLengthFileHeader]
[ChoFixedLengthRecordObject
(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.ThrowAndStop,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false,
ObjectValidationMode = ChoObjectValidationMode.MemberLevel)]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoFixedLengthRecordField(0, 8, ErrorMode = ChoErrorMode.ReportAndContinue )]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, QuoteField = true)]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
public bool TryValidate(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName,
ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
在上述 EmployeeRec
中是数据类。它仅包含特定于领域的属性和操作。将其标记为一个非常简单的类以供查看。
我们将验证、回调机制、配置等分离到元数据类型类 EmployeeRecMeta
中。
16. 配置选项
如果 POCO 实体类是自动生成的类或通过库公开,或者它是一个 sealed
类,则会限制您以声明方式附加 FixedLength
模式定义。在这种情况下,您可以选择以下选项之一来指定 FixedLength
布局配置
- 手动配置
- 自动映射配置
- 附加
MetadataType
类
我将向您展示如何为每种方法配置以下 POCO 实体类。
清单 16.1 密封 POCO 实体类
public sealed class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
16.1 手动配置
从头开始定义一个全新的配置对象,并将所有必要的 FixedLength
字段添加到 ChoFixedLengthConfiguration.FixedLengthRecordFieldConfigurations
集合属性。此选项为您提供了更大的灵活性来控制 FixedLength
解析的配置。但缺点是,如果 FixedLength
文件布局很大,可能会出错并且难以管理。
图 16.1.1 手动配置
ChoFixedLengthRecordConfiguration config = new ChoFixedLengthRecordConfiguration();
config.FixedLengthFileHeaderConfiguration.HasHeaderRecord = true;
config.ThrowAndStopOnMissingField = true;
config.FixedLengthRecordFieldConfigurations.Add
(new ChoFixedLengthRecordFieldConfiguration("Id", 0, 8, fieldType: typeof(int)));
config.FixedLengthRecordFieldConfigurations.Add
(new ChoFixedLengthRecordFieldConfiguration("Name", 8, 10));
16.2 自动映射配置
这是一种替代方法,并且是错误率较低的方法,用于自动映射 POCO 实体类的 FixedLength
列。
首先,为 EmployeeRec
POCO 实体类定义一个模式类,如下所示。
清单 16.2.1 Auto Map 类
public class EmployeeRecMap
{
[ChoFixedLengthRecordField(0, 8, FieldName = "id")]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, FieldName = "Name")]
public string Name { get; set; }
}
然后,您可以使用它通过 ChoFixedLengthRecordConfiguration.MapRecordFields
方法自动映射 FixedLength
列。
清单 16.2.2 使用 Auto Map 配置
ChoFixedLengthRecordConfiguration config = new ChoFixedLengthRecordConfiguration();
config.MapRecordFields<EmployeeRecMap>();
foreach (var e in new ChoFixedLengthReader<EmployeeRec>("Emp.txt", config))
Console.WriteLine(e.ToString());
16.3 附加 MetadataType 类
这是为 POCO 实体对象附加 MetadataType
类的另一种方法。以前的方法只关心 FixedLength
列的自动映射。其他配置属性,如属性转换器、解析器参数、默认/回退值等,均未考虑。
此模型通过定义 MetadataType
类并声明性地指定 FixedLength
配置参数来考虑所有因素。当您的 POCO 实体是 sealed
且不是 partial 类时,这很有用。此外,它是配置 POCO 实体 FixedLength
解析的有利且错误率较低的方法之一。
清单 16.3.1 定义 MetadataType 类
[ChoFixedLengthFileHeader()]
[ChoFixedLengthRecordObject
(Encoding = "Encoding.UTF32", ErrorMode = ChoErrorMode.ReportAndContinue,
IgnoreFieldValueMode = ChoIgnoreFieldValueMode.All, ThrowAndStopOnMissingField = false,
ObjectValidationMode = ChoObjectValidationMode.MemberLevel)]
public class EmployeeRecMeta : IChoNotifyRecordWrite, IChoValidatable
{
[ChoFixedLengthRecordField
(0, 8, FieldName = "id", ErrorMode = ChoErrorMode.ReportAndContinue )]
[ChoTypeConverter(typeof(IntConverter))]
[Range(1, 1, ErrorMessage = "Id must be > 0.")]
//[ChoFallbackValue(1)]
public int Id { get; set; }
[ChoFixedLengthRecordField(8, 10, FieldName = "Name", QuoteField = true)]
[StringLength(1)]
[DefaultValue("ZZZ")]
[ChoFallbackValue("XXX")]
public string Name { get; set; }
public bool AfterRecordFieldWrite(object target, int index, string propName, object value)
{
throw new NotImplementedException();
}
public bool AfterRecordWrite(object target, int index, object source)
{
throw new NotImplementedException();
}
public bool BeforeRecordFieldWrite
(object target, int index, string propName, ref object value)
{
throw new NotImplementedException();
}
public bool BeforeRecordWrite(object target, int index, ref object source)
{
throw new NotImplementedException();
}
public bool BeginWrite(object source)
{
throw new NotImplementedException();
}
public void EndWrite(object source)
{
throw new NotImplementedException();
}
public bool RecordFieldWriteError
(object target, int index, string propName, object value, Exception ex)
{
throw new NotImplementedException();
}
public bool RecordWriteError(object target, int index, object source, Exception ex)
{
throw new NotImplementedException();
}
public bool TryValidate
(object target, ICollection<ValidationResult> validationResults)
{
return true;
}
public bool TryValidateFor
(object target, string memberName,
ICollection<ValidationResult> validationResults)
{
return true;
}
public void Validate(object target)
{
}
public void ValidateFor(object target, string memberName)
{
}
}
清单 16.3.2 附加 MetadataType 类
//Attach metadata
ChoMetadataObjectCache.Default.Attach<EmployeeRec>(new EmployeeRecMeta());
using (var tx = File.OpenWrite("Emp.txt"))
{
using (var parser = new ChoFixedLengthWriter<EmployeeRec>(tx))
{
parser.Write(objs);
}
}
17. ToText 辅助方法
这是一个巧妙的辅助方法,用于从对象列表中生成 FixedLength
格式化输出。它帮助您在测试环境中快速运行和尝试不同的选项以查看 FixedLength
输出。
static void ToTextTest()
{
List<EmployeeRec> objs = new List<EmployeeRec>();
EmployeeRec rec1 = new EmployeeRec();
rec1.Id = 10;
rec1.Name = "Mark";
objs.Add(rec1);
EmployeeRec rec2 = new EmployeeRec();
rec2.Id = 200;
rec2.Name = "Lou";
objs.Add(rec2);
Console.WriteLine(ChoFixedLengthWriter.ToText(objs));
}
18. Writing DataReader 辅助方法
此辅助方法允许您从 ADO.NET DataReader
创建 FixedLength
文件/流。
static void WriteDataReaderTest()
{
string connString = @"Data Source=(localdb)\v11.0;Initial Catalog=TestDb;Integrated Security=True";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
IDataReader dr = cmd.ExecuteReader();
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoFixedLengthWriter(writer, config))
{
parser.Write(dr);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
19. Writing DataTable 辅助方法
此辅助方法允许您从 ADO.NET DataTable
创建 FixedLength
文件/流。
static void WriteDataTableTest()
{
string connString = @"Data Source=(localdb)\v11.0;Initial Catalog=TestDb;Integrated Security=True";
SqlConnection conn = new SqlConnection(connString);
conn.Open();
SqlCommand cmd = new SqlCommand("SELECT * FROM Members", conn);
SqlDataAdapter da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoFixedLengthWriter(writer, config))
{
parser.Write(dt);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
20. 高级主题
20.1 覆盖转换器格式规范
Cinchoo ETL 自动解析并将每个 FixedLength
列值无缝转换为相应的 FixedLength
列的底层数据类型。大多数基本的 .NET 类型都无需任何设置即可自动处理。
这是通过 ETL 系统中的两个关键设置实现的
ChoFixedLengthRecordConfiguration.CultureInfo
- 表示有关特定文化的信息,包括文化的名称、书写系统和所使用的日历,以及访问提供常见操作信息(如日期格式化和字符串排序)的特定文化对象。默认值为 'en-US
'。ChoTypeConverterFormatSpec
- 这是一个全局格式说明符类,包含所有内置 .NET 类型格式说明。
在本节中,我将讨论如何根据解析需求更改每个 .NET 内在数据类型的默认格式规范。
ChoTypeConverterFormatSpec
是一个单例类,其实例通过 'Instance
' static
成员公开。它是线程本地的,这意味着每个线程都会保留一个独立的实例副本。
为每个内置类型提供了 2 组格式说明符成员,一组用于加载,另一组用于写入值,但 Boolean
、Enum
、DataTime
类型除外。这些类型只有一个成员用于加载和写入操作。
通过 ChoTypeConverterFormatSpec
指定每个内置数据类型格式说明符将影响系统范围,即,将 ChoTypeConverterFormatSpec.IntNumberStyle = NumberStyles.AllowParentheses
将影响 FixedLength
对象的所有整数成员以允许括号。如果您想覆盖此行为并控制特定的 FixedLength
数据成员以处理其自己的从全局系统范围设置解析 FixedLength
值的独特方式,可以通过在 FixedLength
字段成员级别指定 TypeConverter
来完成。有关更多信息,请参阅第 13.4 节。
清单 20.1.1 ChoTypeConverterFormatSpec 成员
public class ChoTypeConverterFormatSpec
{
public static readonly ThreadLocal<ChoTypeConverterFormatSpec> Instance =
new ThreadLocal<ChoTypeConverterFormatSpec>(() => new ChoTypeConverterFormatSpec());
public string DateTimeFormat { get; set; }
public ChoBooleanFormatSpec BooleanFormat { get; set; }
public ChoEnumFormatSpec EnumFormat { get; set; }
public NumberStyles? CurrencyNumberStyle { get; set; }
public string CurrencyFormat { get; set; }
public NumberStyles? BigIntegerNumberStyle { get; set; }
public string BigIntegerFormat { get; set; }
public NumberStyles? ByteNumberStyle { get; set; }
public string ByteFormat { get; set; }
public NumberStyles? SByteNumberStyle { get; set; }
public string SByteFormat { get; set; }
public NumberStyles? DecimalNumberStyle { get; set; }
public string DecimalFormat { get; set; }
public NumberStyles? DoubleNumberStyle { get; set; }
public string DoubleFormat { get; set; }
public NumberStyles? FloatNumberStyle { get; set; }
public string FloatFormat { get; set; }
public string IntFormat { get; set; }
public NumberStyles? IntNumberStyle { get; set; }
public string UIntFormat { get; set; }
public NumberStyles? UIntNumberStyle { get; set; }
public NumberStyles? LongNumberStyle { get; set; }
public string LongFormat { get; set; }
public NumberStyles? ULongNumberStyle { get; set; }
public string ULongFormat { get; set; }
public NumberStyles? ShortNumberStyle { get; set; }
public string ShortFormat { get; set; }
public NumberStyles? UShortNumberStyle { get; set; }
public string UShortFormat { get; set; }
}
下面的示例展示了如何使用 FixedLengthReader
加载具有 'se-SE
'(瑞典)特定文化数据的 FixedLength
数据流。此外,输入提要附带包含括号的 'EmployeeNo
' 值。为了使加载成功,我们必须将 ChoTypeConverterFormatSpec.IntNumberStyle
设置为 NumberStyles.AllowParenthesis
。
清单 20.1.2 在代码中使用 ChoTypeConverterFormatSpec
static void FormatSpecDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "d";
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoFixedLengthWriter(writer).WithFirstLineHeader())
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
20.2 货币支持
Cinchoo ETL 提供 ChoCurrency
对象来读取和写入 FixedLength
文件中的货币值。 ChoCurrency
是一个包装类,用于以十进制类型保存货币值,并支持在 FixedLength
加载期间将其序列化为文本格式。
清单 20.2.1 在动态模型中使用 Currency 成员
static void CurrencyDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.CurrencyFormat = "C2";
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser =
new ChoFixedLengthWriter(writer).WithFirstLineHeader().QuoteAllFields())
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
上面的示例展示了如何使用动态对象模型输出货币值。由于货币输出将具有千位逗号分隔符,这将导致无法生成 FixedLength
文件。为了解决这个问题,我们指定写入器引用所有字段。
PS:货币值的格式由 FixedLengthReader 通过 ChoRecordConfiguration.Culture 和 ChoTypeConverterFormatSpec.CurrencyFormat 确定。
下面的示例展示了如何在 POCO 实体类中使用 ChoCurrency FixedLength
字段。
清单 20.2.2 在 POCO 模型中使用 Currency 成员
public class EmployeeRecWithCurrency
{
public int Id { get; set; }
public string Name { get; set; }
public ChoCurrency Salary { get; set; }
}
static void CurrencyPOCOTest()
{
List<EmployeeRecWithCurrency> objs = new List<EmployeeRecWithCurrency>();
EmployeeRecWithCurrency rec1 = new EmployeeRecWithCurrency();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
EmployeeRecWithCurrency rec2 = new EmployeeRecWithCurrency();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser =
new ChoFixedLengthWriter<EmployeeRecWithCurrency>(writer).WithFirstLineHeader().
WithField("Id", 0, 5).
WithField("Name", 5, 20).
WithField("Salary", 25, 10))
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
20.3 枚举支持
Cinchoo ETL 隐式处理从 FixedLength
文件中解析/写入 enum
列值。如果您想精细控制这些值的解析,可以通过 ChoTypeConverterFormatSpec.EnumFormat
全局指定它们。默认值为 ChoEnumFormatSpec.Value
。
FYI,更改此值将影响整个系统。
有 3 种可能的值可以使用
ChoEnumFormatSpec.Value
-Enum
值用于解析ChoEnumFormatSpec.Name
-Enum
键名用于解析ChoEnumFormatSpec.Description
- 如果每个enum
键都使用DescriptionAttribute
装饰,则其值将用于解析
清单 20.3.1 在解析期间指定 Enum 格式规范
public enum EmployeeType
{
[Description("Full Time Employee")]
Permanent = 0,
[Description("Temporary Employee")]
Temporary = 1,
[Description("Contract Employee")]
Contract = 2
}
static void EnumTest()
{
ChoTypeConverterFormatSpec.Instance.EnumFormat = ChoEnumFormatSpec.Description;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoFixedLengthWriter(writer).WithFirstLineHeader().
WithField("Id", 0, 5).
WithField("Name", 5, 20).
WithField("JoinedDate", 25, 10).
WithField("IsActive", 35, 1).
WithField("Salary", 36, 10).
WithField("Status", 46, 10)
)
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
20.4 布尔值支持
Cinchoo ETL 隐式处理从 FixedLength
文件中解析/写入布尔 FixedLength
列值。如果您想精细控制这些值的解析,可以通过 ChoTypeConverterFormatSpec.BooleanFormat
全局指定它们。默认值为 ChoBooleanFormatSpec.ZeroOrOne
。
FYI,更改此值将影响整个系统。
有 4 种可能的值可以使用
ChoBooleanFormatSpec.ZeroOrOne
- '0
' 表示false
。'1
' 表示true
。ChoBooleanFormatSpec.YOrN
- 'Y
' 表示true
,'N
' 表示false
。ChoBooleanFormatSpec.TrueOrFalse
- 'True
' 表示true
,'False
' 表示false
。ChoBooleanFormatSpec.YesOrNo
- 'Yes
' 表示true
,'No
' 表示false
。
清单 20.4.1 在解析期间指定布尔值格式规范
static void BoolTest()
{
ChoTypeConverterFormatSpec.Instance.BooleanFormat = ChoBooleanFormatSpec.YOrN;
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
rec1.Status = EmployeeType.Permanent;
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
rec2.Status = EmployeeType.Contract;
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser =
new ChoFixedLengthWriter(writer).WithFirstLineHeader().WithField("Id", 0, 5).
WithField("Name", 5, 20).
WithField("JoinedDate", 25, 10).
WithField("IsActive", 35, 1).
WithField("Salary", 36, 10).
WithField("Status", 46, 10)
)
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
20.5 DateTime 支持
Cinchoo ETL 隐式处理使用系统 Culture
或自定义设置的文化从 FixedLength
文件中解析/写入 datetime FixedLength
列值。如果您想精细控制这些值的解析,可以通过 ChoTypeConverterFormatSpec.DateTimeFormat
全局指定它们。默认值为 'd
'。
FYI,更改此值将影响整个系统。
您可以使用任何有效的标准或自定义 datetime .NET 格式规范来解析文件中的 datetime FixedLength
值。
清单 20.5.1 在解析期间指定 datetime 格式规范
static void DateTimeDynamicTest()
{
ChoTypeConverterFormatSpec.Instance.DateTimeFormat = "MMM dd, yyyy";
List<ExpandoObject> objs = new List<ExpandoObject>();
dynamic rec1 = new ExpandoObject();
rec1.Id = 10;
rec1.Name = "Mark";
rec1.JoinedDate = new DateTime(2001, 2, 2);
rec1.IsActive = true;
rec1.Salary = new ChoCurrency(100000);
objs.Add(rec1);
dynamic rec2 = new ExpandoObject();
rec2.Id = 200;
rec2.Name = "Lou";
rec2.JoinedDate = new DateTime(1990, 10, 23);
rec2.IsActive = false;
rec2.Salary = new ChoCurrency(150000);
objs.Add(rec2);
using (var stream = new MemoryStream())
using (var reader = new StreamReader(stream))
using (var writer = new StreamWriter(stream))
using (var parser = new ChoFixedLengthWriter(writer).WithFirstLineHeader().
WithField("Name", 5, 20).
WithField("JoinedDate", 25, 10).
WithField("IsActive", 35, 1).
WithField("Salary", 36, 10).
WithField("Status", 46, 10)
)
{
parser.Write(objs);
writer.Flush();
stream.Position = 0;
Console.WriteLine(reader.ReadToEnd());
}
}
上面的示例展示了如何将自定义 datetime
值生成到 FixedLength
文件中。
注意:由于 datetime
值包含 FixedLength
分隔符,我们指示写入器引用所有字段。
有关 Cinchoo ETL 的更多信息,请访问其他 CodeProject 文章。