【问题标题】:db.SaveChanges() in loop doesn't save records after an error循环中的 db.SaveChanges() 出错后不保存记录
【发布时间】:2016-06-18 20:22:21
【问题描述】:

我想使用 EPPLUS 将数据从 Excel 导入数据库。从这里我拿了代码:https://www.paragon-inc.com/resources/blogs-posts/easy_excel_interaction_pt6

using (var db = new DbEntities())
{
    for (var row = 2; row <= lastRow; row++)
    {
        var newRecord = new DB_USER
        {
            ID = Int32.Parse(worksheet.Cells[idColumn + row].Value.ToString()),
            FIRST_NAME = worksheet.Cells[firstNameColumn + row].Value.ToString(),
            LAST_NAME = worksheet.Cells[lastNameColumn + row].Value.ToString(),                               
        };

        db.DB_USER.Add(newRecord);
        try
        {
            db.SaveChanges();
            totalImported++;
        }
        catch (Exception ex)
        {
            resultMessages.Add(string.Format("Error in line #{0}: {1}\n", row,
                ex.Message));
        }
    }
}

如果 excel 中的数据正确,一切正常。问题是是否有任何记录包含无效数据。例如,我们在 excel 中有 3 条记录:

  • ID:21 (ID 不在基数中)|名字:约翰 |姓氏:凯奇
  • ID:1 (ID 在基数中) |名字:梅 |姓氏:蓝色
  • ID:25 (ID 不在基数中) |名字:尼克 |姓氏:Siri

并且在数据库中已经记录了ID = 1。所以第一和第三应该保存,但第二不应该。问题是只有第一条记录在保存,其余的(第二条和第三条)会出错。我不知道为什么?也许是因为这是一笔交易还是什么?有点奇怪。谁能告诉我我应该怎么做才能保存第一和第三条记录?在这种情况下不仅是第一名吗?


错误:

ORA-00001: 违反唯一约束主键

第三条记录中没有任何意义的东西......

【问题讨论】:

  • 具体会收到什么错误?如果您遗漏关键信息,我们将无法真正帮助您。
  • @mason,已编辑,现在您可以看到错误但仍然没有意义
  • @DiPix:你搞定了吗?
  • @MikaelPuusaari,是的,你为什么要问?
  • 哦,既然问题还没有解决,如果有人给你答案,就把它标记为答案,否则如果你自己找到答案,添加答案并将你的答案标记为答案,这样问题可能已关闭:)

标签: c# asp.net-mvc entity-framework epplus ora-00001


【解决方案1】:

为什么不检查数据库,看看数据库中是否已经存在具有特定 ID 的用户,并且仅在用户不存在时才进行插入:

  bool exists = db.DB_USER.Where(u => u.ID == newRecord.ID).Any();

    if(!exists)
    {
        //Do the insert
    }

【讨论】:

  • 当然可以,但是如果有更复杂的外键呢?
  • 通常外键引用不会导致插入问题,例如,如果您的用户对象上有一个名为 UserTypeID 的属性,您可以使用 UserTypeID=1 插入任意数量的用户。您是由于主键违规而出现错误,因此我为您提供了解决问题的方法
  • 你是对的,但是如果在 excel 中的数据在 ID 列中将是字符串而不是 int 怎么办? Try catch 将处理这个问题。在您的解决方案中,我必须使用 linq 来处理这个问题。你知道我的意思吗?
  • 不,我不明白你的意思。而不是先解决一个问题,而不是先解决一个问题。这是我给你解决方案的主键违规问题,如果你得到任何进一步的错误之后尝试修复它们
  • 如果它是一个字符串,你需要在处理它之前转换为 int,你只需 Int.Parse(ID),如果它是一个 int,你需要转换为字符串,你使用 ID.Tostring( )
【解决方案2】:

您可能会发现自己处于违反 PK 之外的其他约束的情况。 这有可能大大增加在添加/更新之前验证所有数据的工作量。 对于这些你不能简单地检查记录是否已经存在的场景,我发现一旦发生这样的错误,EF核心会继续抛出错误,因为它将实体状态保持为“已修改”。

具体你的情况,试试这个:

        try
        {
            db.SaveChanges();
            totalImported++;
        }
        catch (Exception ex)
        {
            resultMessages.Add(string.Format("Error in line #{0}: {1}\n", row,
                ex.Message));
            db.Entry(db.DB_USER).State = EntityState.Unchanged;
        }

【讨论】:

    【解决方案3】:

    您根本不应该在代码中设置 ID。在数据库中,将 ID 设置为 autoincrement.. 当然仍然在您的模型中,但不在您的逻辑中

    这样,您无需考虑处理 ID,而是让实体框架处理它

    无论如何,这里有一些关于如何更改代码的想法:

            using (var db = new DbEntities())
                {
                    foreach (user in db)
                    {
                        var userExists = db.DB_USER.Where(u => u.ID == user.ID);
    
                        if(!userExists)
                        {
                            var newUser = new DB_USER
                            {
                                    ID = Int32.Parse(worksheet.Cells[idColumn + row].Value.ToString()),
                                    FIRST_NAME = worksheet.Cells[firstNameColumn + row].Value.ToString(),
                                    LAST_NAME = worksheet.Cells[lastNameColumn + row].Value.ToString(),                               
                            };
    
                            try
                            {
                                db.DB_USER.Add(newUser);
                                db.SaveChanges();
                                totalImported++;
                            }
                            catch (Exception ex)
                            {
                                 resultMessages.Add(string.Format("Error in line #{0}: {1}\n", row,
                                 ex.Message));
                            }
                        }
                    }
                }
    

    我的猜测仍然是因为您在问题中的代码是从第三行开始的(这似乎是 ID 21 在你改变它之前,它可能会让你认为它是第一行运行良好。

    如果您刷新数据库,在您更改后,数据库中可能已经有一个 ID 为 21 的条目 它到 25,然后运行您现在可能也有一个 25 的代码,并且再次遇到相同的错误。

    我在代码中添加了一个 foreach 循环并检查条目是否存在,因此代码应避免主 密钥违规。

    我还将它移到 try-catch 块中:

    db.DB_USER.Add(newUser);
    

    【讨论】:

    • 这不是我预期的答案。因为它不是我的数据库,我无法更改结构。
    • 好的,第二个和第三个给出错误的原因可能是因为它们已经存在..第一行一开始不存在,但是当你创建它时,第三行不能添加,因为你已经在第一行中添加了具有该 ID 的行。正如您指出的那样,第二行 ID 事先已经存在哦,您现在更新了问题以包含错误..这是主键违规,主键可能是 ID ..您不能添加带有主键的行已经存在,因此您需要先检查它是否存在,然后再尝试添加行
    • 哦,上面的帖子有错误。第 1 条和第 3 条记录具有不同的 ID,并且在基数中不是。
    • 在添加它们之前,您仍然需要检查数据库中是否已经存在每个行 ID,因为您似乎在数据库中有一个主键,通过查看您的数据和代码,主键好像是ID列。。不能添加已经存在的主键,否则主键违规,数据库不会接受
    • 我重写了您的代码并添加了更改(我无法访问 Visual Studio atm,所以我必须从内存中编写代码可能容易出错,它们应该不难你来修复),试图尽可能容易地解释更改背后的原因。此外,.NET 框架没有编写整个异常消息,而是提供了很好的可能性来使用 innerException 捕获好的异常,youtube 上有很好的教程
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2021-11-24
    • 2012-08-31
    • 2018-05-24
    • 1970-01-01
    • 2018-06-20
    相关资源
    最近更新 更多