You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在C#中将CSV导入SQL Server Express并转换数据类型

问题分析与修正方案

你的代码存在几个关键问题,会导致导入失败或数据类型转换错误,以下是具体问题和修复后的实现:

核心问题梳理

  • CsvReader初始化冗余:无需将StreamReader强制转换为IParser,CsvReader构造函数直接支持TextReader类型参数。
  • 日期转换不规范:SqlDateTime.Parse是旧版SQL专属方法,针对ISO 8601格式(YYYY-MM-DDTHH:mm:ssZ)的日期,推荐使用标准DateTime.ParseExact确保转换准确性,同时需处理空值场景。
  • SqlBulkCopy数据源错误:
    • 读取完成后csv1已被using块释放,此时调用WriteToServer会抛出资源已释放异常;
    • StreamReader不能直接转换为IDataReader,第二个CSV文件同样需要通过CsvReader读取并转换为合法数据源。
  • 文件资源冲突:同时打开两个StreamReader可能触发文件锁定,建议分开处理不同CSV文件。

修正后的代码实现

1. 定义实体类(确保类型匹配)

public class TicketCSV
{
    public int TicketID { get; set; }
    public string TicketTitle { get; set; }
    public string TicketStatus { get; set; }
    public string CustomerName { get; set; }
    public string TechnicianFullName { get; set; }
    public string TicketResolvedDate { get; set; } // 先以字符串读取,后续转换为DateTime
}

// 第二个CSV对应的实体类(根据WerkUren表头定义)
public class WorkHourCSV
{
    // 按实际CSV表头定义属性
    // 示例:public int HourID { get; set; }
    // public string TicketID { get; set; }
    // ...
}

2. 读取CSV并批量导入SQL Server

// 处理第一个CSV:GeslotenTickets
List<TicketCSV> tickets = new List<TicketCSV>();
using (var reader1 = new StreamReader(OutputClosedTickets))
using (var csv1 = new CsvReader(reader1, CultureInfo.InvariantCulture))
{
    csv1.Configuration.Delimiter = ",";
    csv1.Configuration.MissingFieldFound = null;
    csv1.Configuration.PrepareHeaderForMatch = (header, index) => header.ToLower();

    // 强类型读取CSV数据
    tickets = csv1.GetRecords<TicketCSV>().ToList();
}

// 转换为DataTable适配SqlBulkCopy
DataTable ticketTable = new DataTable();
ticketTable.Columns.Add("TicketID", typeof(int));
ticketTable.Columns.Add("TicketTitle", typeof(string));
ticketTable.Columns.Add("TicketStatus", typeof(string));
ticketTable.Columns.Add("CustomerName", typeof(string));
ticketTable.Columns.Add("TechnicianFullName", typeof(string));
ticketTable.Columns.Add("TicketResolvedDate", typeof(DateTime));

foreach (var ticket in tickets)
{
    var row = ticketTable.NewRow();
    row["TicketID"] = ticket.TicketID;
    row["TicketTitle"] = ticket.TicketTitle;
    row["TicketStatus"] = ticket.TicketStatus;
    row["CustomerName"] = ticket.CustomerName;
    row["TechnicianFullName"] = ticket.TechnicianFullName;
    
    // 处理日期转换,兼容空值场景
    row["TicketResolvedDate"] = string.IsNullOrEmpty(ticket.TicketResolvedDate)
        ? DBNull.Value
        : DateTime.ParseExact(ticket.TicketResolvedDate, "yyyy-MM-ddTHH:mm:ssZ", CultureInfo.InvariantCulture);
    
    ticketTable.Rows.Add(row);
}

// 批量导入GeslotenTickets表
using (var bulkCopy = new SqlBulkCopy(connectionString))
{
    bulkCopy.DestinationTableName = "GeslotenTickets";
    // 显式列映射(确保CSV列与数据库列一一对应)
    bulkCopy.ColumnMappings.Add("TicketID", "TicketID");
    bulkCopy.ColumnMappings.Add("TicketTitle", "TicketTitle");
    bulkCopy.ColumnMappings.Add("TicketStatus", "TicketStatus");
    bulkCopy.ColumnMappings.Add("CustomerName", "CustomerName");
    bulkCopy.ColumnMappings.Add("TechnicianFullName", "TechnicianFullName");
    bulkCopy.ColumnMappings.Add("TicketResolvedDate", "TicketResolvedDate");
    
    bulkCopy.WriteToServer(ticketTable);
}

// 处理第二个CSV:WerkUren
using (var reader2 = new StreamReader(OutputWorkhours))
using (var csv2 = new CsvReader(reader2, CultureInfo.InvariantCulture))
{
    csv2.Configuration.Delimiter = ",";
    csv2.Configuration.MissingFieldFound = null;
    csv2.Configuration.PrepareHeaderForMatch = (header, index) => header.ToLower();

    var workHours = csv2.GetRecords<WorkHourCSV>().ToList();
    DataTable workHourTable = new DataTable();
    
    // 按WerkUren数据库表结构添加列
    // 示例:workHourTable.Columns.Add("HourID", typeof(int));
    // workHourTable.Columns.Add("TicketID", typeof(string));
    // ...
    
    foreach (var hour in workHours)
    {
        var row = workHourTable.NewRow();
        // 填充行数据
        // 示例:row["HourID"] = hour.HourID;
        // row["TicketID"] = hour.TicketID;
        // ...
        workHourTable.Rows.Add(row);
    }

    using (var bulkCopy = new SqlBulkCopy(connectionString))
    {
        bulkCopy.DestinationTableName = "WerkUren";
        // 添加对应列映射
        // ...
        bulkCopy.WriteToServer(workHourTable);
    }
}

额外注意事项

  • 确保数据库表GeslotenTickets的列类型匹配:TicketID为int,TicketResolvedDate建议使用datetime2(比datetime支持更广的日期范围)。
  • 如果CSV中存在特殊字符或换行,可在CsvReader.Configuration中配置TrimOptions处理多余空格。

内容的提问来源于stack exchange,提问作者NightBladium

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 18:12:12