将 C# DataTable(ColumnName, ColumnType, ColumnValue) 转换为 csv 并恢复
Convert C# DataTable(ColumnName, ColumnType, ColumnValue) to csv and restore back
我正在寻找将 ADO.NET 数据 table 转换为 csv 文件的 c# 代码,但是我想 save/restore
- columns name,
- column data Type and
- column Value
在 csv 中。我发现的大多数解决方案都是从字符串列类型的 CSV 中恢复数据 table。我还希望可以将可为 null 的值恢复为 DBNull.Value。 DateTime 列应仅保存和恢复为 DateTime 类型。这个概念是使用 Oracle/Sqlserver 数据库中的 DataAdapter 填充数据 table,然后将 table 保存到 CSV 文件,然后从 CSV 恢复。
我使用下面的代码 link 使用 DataTableExtensions class c# datatable to csv
将 DataTable 保存到 CSV 文件
为了将 CSV 文件读回 DataTable,我使用了下面的 Link
http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F
问题是当我将 CSV 文件恢复为数据时table我必须从 DataTable 行创建实体。但是他们在 InvalidCast 上抛出异常。
假设你想在第一行存储列名,在第二行存储类型,数据在第三行开始,你可以使用下面的代码。示例数据:
DataTable tblExport = new DataTable();
tblExport.Columns.Add("ID", typeof(int));
tblExport.Columns.Add("Name", typeof(string));
tblExport.Columns.Add("DateofBirth", typeof(DateTime)).AllowDBNull = false;
tblExport.Columns.Add("DateofDeath", typeof(DateTime)).AllowDBNull = true;
tblExport.Rows.Add(1, "Tim", new DateTime(1973, 7, 9), DBNull.Value);
tblExport.Rows.Add(2, "Jim", new DateTime(1953, 3, 19), new DateTime(2011, 1, 2));
tblExport.Rows.Add(3, "Toby", new DateTime(1983, 4, 23), DBNull.Value);
由于您需要使用 value.ToString
将所有值转换为字符串,因此我在开始时将区域性更改为 InvariantCulture
以强制使用特定的 DateTime 格式,存储旧的以便您可以启用最后再说一遍。我希望代码是不言自明的:
var oldCulture = CultureInfo.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
将 DataTable 写入 csv 文件
string delimiter = "\t"; // tab separated
StringBuilder sb = new StringBuilder();
// first line column-names
IEnumerable<string> columnNames = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.ColumnName);
sb.AppendLine(string.Join(delimiter, columnNames));
// second line column-types
IEnumerable<string> columnTypes = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.DataType.ToString());
sb.AppendLine(string.Join(delimiter, columnTypes));
// rest: table data
foreach (DataRow row in tblExport.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(delimiter, fields));
}
string path = @"C:\Temp\Testfile.csv";
File.WriteAllText(path, sb.ToString());
将 csv 文件读入 DataTable
string[] lines = File.ReadAllLines(path);
string[] columns = lines[0].Split(new[] { delimiter }, StringSplitOptions.None);
string[] types = lines[1].Split(new[] { delimiter }, StringSplitOptions.None);
DataTable tblImport = new DataTable();
for (int i = 0; i < columns.Length; i++)
{
string colName = columns[i];
string typeName = types[i];
tblImport.Columns.Add(colName, Type.GetType(typeName));
}
// import data
// use a typeValueConverter dictionary to convert values:
var typeValueConverter = new Dictionary<Type, Func<string, object>> {
{ typeof(DateTime), value => value.TryGetDateTime(null, null) },
{ typeof(Decimal), value => value.TryGetDecimal(null) },
{ typeof(int), value => value.TryGetInt32(null) },
};
foreach (string line in lines.Skip(2))
{
string[] fields = line.Split(new[]{ delimiter }, StringSplitOptions.None);
DataRow r = tblImport.Rows.Add(); // already added at this point
for (int i = 0; i < tblImport.Columns.Count; i++)
{
DataColumn col = tblImport.Columns[i];
string rawValue = fields[i];
object val = rawValue;
if (typeValueConverter.ContainsKey(col.DataType))
val = typeValueConverter[col.DataType](rawValue);
else if (col.DataType != typeof(string) && string.IsNullOrEmpty(rawValue))
val = DBNull.Value;
r.SetField(col, val);
}
}
System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture;
当然你应该在两种方法中将两者分开,一种用于导出,一种用于导入。
我使用了我的扩展方法 TryGetDateTime
、TryGetDecimal
和 TryGetInt32
,它们将字符串解析为 DateTime?
、Decimal?
和 int?
(如果无法解析则为 null)。它们在 LINQ 查询中特别方便:
public static DateTime? TryGetDateTime(this string item, DateTimeFormatInfo dfi, params string[] allowedFormats)
{
if (dfi == null) dfi = DateTimeFormatInfo.InvariantInfo;
DateTime dt;
bool success;
if(allowedFormats == null)
success = DateTime.TryParse(item, dfi, DateTimeStyles.None, out dt);
else
success = DateTime.TryParseExact(item, allowedFormats, dfi, DateTimeStyles.None, out dt);
if (success) return dt;
return null;
}
public static decimal? TryGetDecimal(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
decimal d = 0m;
bool success = decimal.TryParse(item, nStyles, formatProvider, out d);
if (success)
return d;
else
return null;
}
public static int? TryGetInt32(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
int i = 0;
bool success = int.TryParse(item, nStyles, formatProvider, out i);
if (success)
return i;
else
return null;
}
我正在寻找将 ADO.NET 数据 table 转换为 csv 文件的 c# 代码,但是我想 save/restore
- columns name,
- column data Type and
- column Value
在 csv 中。我发现的大多数解决方案都是从字符串列类型的 CSV 中恢复数据 table。我还希望可以将可为 null 的值恢复为 DBNull.Value。 DateTime 列应仅保存和恢复为 DateTime 类型。这个概念是使用 Oracle/Sqlserver 数据库中的 DataAdapter 填充数据 table,然后将 table 保存到 CSV 文件,然后从 CSV 恢复。
我使用下面的代码 link 使用 DataTableExtensions class c# datatable to csv
将 DataTable 保存到 CSV 文件为了将 CSV 文件读回 DataTable,我使用了下面的 Link http://www.codeproject.com/Articles/11698/A-Portable-and-Efficient-Generic-Parser-for-Flat-F
问题是当我将 CSV 文件恢复为数据时table我必须从 DataTable 行创建实体。但是他们在 InvalidCast 上抛出异常。
假设你想在第一行存储列名,在第二行存储类型,数据在第三行开始,你可以使用下面的代码。示例数据:
DataTable tblExport = new DataTable();
tblExport.Columns.Add("ID", typeof(int));
tblExport.Columns.Add("Name", typeof(string));
tblExport.Columns.Add("DateofBirth", typeof(DateTime)).AllowDBNull = false;
tblExport.Columns.Add("DateofDeath", typeof(DateTime)).AllowDBNull = true;
tblExport.Rows.Add(1, "Tim", new DateTime(1973, 7, 9), DBNull.Value);
tblExport.Rows.Add(2, "Jim", new DateTime(1953, 3, 19), new DateTime(2011, 1, 2));
tblExport.Rows.Add(3, "Toby", new DateTime(1983, 4, 23), DBNull.Value);
由于您需要使用 value.ToString
将所有值转换为字符串,因此我在开始时将区域性更改为 InvariantCulture
以强制使用特定的 DateTime 格式,存储旧的以便您可以启用最后再说一遍。我希望代码是不言自明的:
var oldCulture = CultureInfo.CurrentCulture;
System.Threading.Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
将 DataTable 写入 csv 文件
string delimiter = "\t"; // tab separated
StringBuilder sb = new StringBuilder();
// first line column-names
IEnumerable<string> columnNames = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.ColumnName);
sb.AppendLine(string.Join(delimiter, columnNames));
// second line column-types
IEnumerable<string> columnTypes = tblExport.Columns.Cast<DataColumn>()
.Select(column => column.DataType.ToString());
sb.AppendLine(string.Join(delimiter, columnTypes));
// rest: table data
foreach (DataRow row in tblExport.Rows)
{
IEnumerable<string> fields = row.ItemArray.Select(field => field.ToString());
sb.AppendLine(string.Join(delimiter, fields));
}
string path = @"C:\Temp\Testfile.csv";
File.WriteAllText(path, sb.ToString());
将 csv 文件读入 DataTable
string[] lines = File.ReadAllLines(path);
string[] columns = lines[0].Split(new[] { delimiter }, StringSplitOptions.None);
string[] types = lines[1].Split(new[] { delimiter }, StringSplitOptions.None);
DataTable tblImport = new DataTable();
for (int i = 0; i < columns.Length; i++)
{
string colName = columns[i];
string typeName = types[i];
tblImport.Columns.Add(colName, Type.GetType(typeName));
}
// import data
// use a typeValueConverter dictionary to convert values:
var typeValueConverter = new Dictionary<Type, Func<string, object>> {
{ typeof(DateTime), value => value.TryGetDateTime(null, null) },
{ typeof(Decimal), value => value.TryGetDecimal(null) },
{ typeof(int), value => value.TryGetInt32(null) },
};
foreach (string line in lines.Skip(2))
{
string[] fields = line.Split(new[]{ delimiter }, StringSplitOptions.None);
DataRow r = tblImport.Rows.Add(); // already added at this point
for (int i = 0; i < tblImport.Columns.Count; i++)
{
DataColumn col = tblImport.Columns[i];
string rawValue = fields[i];
object val = rawValue;
if (typeValueConverter.ContainsKey(col.DataType))
val = typeValueConverter[col.DataType](rawValue);
else if (col.DataType != typeof(string) && string.IsNullOrEmpty(rawValue))
val = DBNull.Value;
r.SetField(col, val);
}
}
System.Threading.Thread.CurrentThread.CurrentCulture = oldCulture;
当然你应该在两种方法中将两者分开,一种用于导出,一种用于导入。
我使用了我的扩展方法 TryGetDateTime
、TryGetDecimal
和 TryGetInt32
,它们将字符串解析为 DateTime?
、Decimal?
和 int?
(如果无法解析则为 null)。它们在 LINQ 查询中特别方便:
public static DateTime? TryGetDateTime(this string item, DateTimeFormatInfo dfi, params string[] allowedFormats)
{
if (dfi == null) dfi = DateTimeFormatInfo.InvariantInfo;
DateTime dt;
bool success;
if(allowedFormats == null)
success = DateTime.TryParse(item, dfi, DateTimeStyles.None, out dt);
else
success = DateTime.TryParseExact(item, allowedFormats, dfi, DateTimeStyles.None, out dt);
if (success) return dt;
return null;
}
public static decimal? TryGetDecimal(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
decimal d = 0m;
bool success = decimal.TryParse(item, nStyles, formatProvider, out d);
if (success)
return d;
else
return null;
}
public static int? TryGetInt32(this string item, IFormatProvider formatProvider = null, NumberStyles nStyles = NumberStyles.Any)
{
if (formatProvider == null) formatProvider = NumberFormatInfo.InvariantInfo;
int i = 0;
bool success = int.TryParse(item, nStyles, formatProvider, out i);
if (success)
return i;
else
return null;
}