使用 C#、EF 和 SQL 服务器从 Excel 文件仅插入不存在的行到数据库中

Insert into database from an Excel file only rows that don't exist, using C#, EF, and SQL Server

我读取了一个 Excel 文件并将该数据插入数据库 table,但每次我这样做时,它都会添加现有行和新数据,我只想插入table 中尚不存在的行,我的唯一 ID 是当前时间戳。

例如,这是我第一次插入时当前发生的情况:

ExcelFile                           Database Table

a | b | date                        a | b | date
-----------                        ---------------
1 | 1 | 2018/02/12                  1 | 1 | 2018/02/12  
2 | 2 | 2018 /03/12                 2 | 2 | 2018 /03/12 

当我进行第二次插入时会发生这种情况:

ExcelFile                           Database Table

a | b | date                        a | b | date
-----------                        ---------------
1 | 1 | 2018/02/12                  1 | 1 | 2018/02/12  
2 | 2 | 2018 /03/12                 2 | 2 | 2018 /03/12 
3 | 3 | 2018 /04/12                 1 | 1 | 2018/02/12
                                    2 | 2 | 2018 /03/12 
                                    3 | 3 | 2018 /04/12

我使用 Entity Framework 来执行这个和 ExcelDataReader 包:

var result = reader.AsDataSet();

DataTable dt = new DataTable();
dt = result.Tables[0];      // here I store the data from the Excel file

foreach (DataRow row in dt.Rows)
{
    using (AppContext context = new AppContext())
    {
        Data data = new Data();
        string date = row.ItemArray[4].ToString();
        DateTime parseDate = DateTime.Parse(date);
        Data datos = new Data
                            {
                                a = row.ItemArray[0].ToString(),
                                b = row.ItemArray[1].ToString(),
                                c = row.ItemArray[2].ToString(),
                                d = row.ItemArray[3].ToString(),
                                e = parseDate
                            };
        context.Data.Add(datos);
        context.SaveChanges();
    }
}

有没有办法过滤 excel 文件或比较它们?

我洗耳恭听

在添加之前检查现有行。应在计算 parseDate.

的下方插入以下内容
var existingRow = context.Data.FirstOrDefault(d=>d.e == parseDate); //Note that the ".e" should refer to your "date" field
if (existingRow != null)
{
  //This row already exists
}
else
{
  //It doesn't exist, go ahead and add it
}

如果 "a" 是 table 上的 PK 并且跨行唯一,那么我会在插入之前通过 ID 检查现有行的存在。与迈克的回应类似,尽管一个考虑因素是 table 是否有多个列我会避免返回实体,而只是使用 .Any()

进行存在检查
if (!context.Data.Any(x => x.a == row.a)
  // insert the row as a new entity

这里需要注意的是,如果 excel 文件包含编辑、数据更改的现有行,则此文件将不适用。

对于批量导入流程,我通常会首先将 excel 数据暂存到暂存 table 中来处理这些流程。 (在每次导入之前清除暂存 table)从那里我会将实体映射到暂存 table,而实体则映射到 "real" table。如果有一个 "modified date" 可以从文件中为每条记录提取,那么我也会将导入 date/time 作为应用程序的一部分存储,以便在选择要从暂存中导入的行时 table,仅获取修改 date/time > 最后导入 date/time 的记录。从那里您可以批量查询暂存 table 中的数据,并查找新记录与现有修改。我发现在迁移的两边查询实体比处理导入的内存块更灵活。对于小的导入可能不值得,但对于较大的文件,您需要使用较小的子集和过滤,它可以使事情变得更容易。

在@MikeH 的帮助下,我可以准确地执行我需要的操作 这样,只添加了具有不同 DateTime 的行(在我的例子中,DateTime 始终是升序值。)

foreach (DataRow row in dt.Rows) // dt = my dataTable loaded with ExcelDataReader
                    {
                        using (AppContext context = new AppContext())
                        {
                            string date = row.ItemArray[4].ToString(); 
                            DateTime parseDate = DateTime.Parse(date); // I did a parse because the column "e" only accepted DateTime and not String types.

                            var existingRow = context.Data.FirstOrDefault(d => d.e == parseDate);
                            if (existingRow != null)
                            {
                                Console.WriteLine("Do Nothing");
                            }
                            else
                            {
                                Data datos = new Data
                                {
                                    a = row.ItemArray[0].ToString(),
                                    b = row.ItemArray[1].ToString(),
                                    c = row.ItemArray[2].ToString(),
                                    d = row.ItemArray[3].ToString(),
                                    e = parseDate
                                };
                                context.Data.Add(datos);
                                context.SaveChanges();
                            }
                        }
                    }