【问题标题】:Saving or updating data using ef multiple entity使用 ef 多个实体保存或更新数据
【发布时间】:2013-09-09 03:47:09
【问题描述】:

我在编写涉及从文件插入数据或更新(如果存在)的代码时遇到了一个大问题。我正在使用实体框架。 文件接近 16 000 行

我试图插入它们,但我花了很多时间这样做:超过 5 小时 x 文件。

我决定改变它,但我不知道如何开始。

我读了一些关于保存每 1000、5000、10000 行的内容,但是当我尝试这样做时遇到了异常。

Store update, insert, or delete statement affected an unexpected number of rows (0).
Entities may have been modified or deleted since entities were loaded. Refresh
ObjectStateManager entries.

当我试图修复它时,我得到了这个异常

AcceptChanges can not continue because the object's key values ​​conflict with another
object in ObjectStateManager. Make sure that the key values ​​are unique before calling 
AcceptChanges.

这是我的代码

//before this I passed db data to lists
foreach (var record in records)
{
    if (record != null)
    {
        try
        {
            if (!_location.Any(s => s.Id.Trim() == record.Id))
            {               
                Location l = new Location
                    {
                        Id = record.Id,
                        Name = record.Name,
                        Address = record.Address,
                        City = record.City,
                        State = record.State,
                        Zip = record.Zip.ToString(),
                        Zip2 = record.Zip2,
                        Custom1 = record.Custom1,                       
                        CreatedDate = date,
                        UpdatedDate = date
                    };

                db.location.Add(l);  
                db.SaveChanges();                              
                _location.Add(l);                                        
            }
            else
            {                                
                if (!_sales.Any(s => s.LocationId == record.Id && s.ReportDate == record.ReportDate))
                {
                    Sale s = new Sale
                    {
                        ReportDate = record.ReportDate,
                        CreatedDate = date,
                        UpdatedDate = date,
                        Amount = record.Amount,
                        LocationId = record.Id,
                    };
                    db.Sale.Add(s);  
                    db.SaveChanges();                  
                    _sales.Add(s);
                }
                else
                {

                    if (!_sales.Any(s => s.LocationId == record.Id && s.ReportDate == record.ReportDate && s.Amount == record.Amount))
                    {                       
                        if ((_sales.Where(s => s.LocationId == record.Id && s.ReportDate == record.ReportDate)).Count() == 1)
                        {                           
                            var sale = _sales.SingleOrDefault(s => s.LocationId == record.Id && s.ReportDate == record.ReportDate);
                            sale.UpdatedDate = date;
                            sale.Amount = record.Amount;
                            db.Entry(sale).State = EntityState.Modified;  
                            db.SaveChanges();                                                                   
                        }                                                
                    }
                }                                        
            }                        
        }
        catch (Exception ex)
        {

        }

    }
}

我所做的更改是:

在 foreach 结束时仅将所有 db.SaveChanges 更改为一个,我得到了第一个异常(当我将它们按 3000 分组时)

更改实体状态,我得到了第二个异常。

感谢您的帮助,

谢谢。

【问题讨论】:

  • 你能显示异常堆栈跟踪吗?
  • 您是否尝试使用只有两条记录的测试文件(例如,三种情况中的每一种)来隔离三个 if-if-else 块中的哪一个导致异常?跨度>

标签: c# asp.net-mvc-4 entity-framework-4


【解决方案1】:

据我了解,您希望将数据从文件导入数据。如果我是对的,我也面临同样的问题,但每次更新有超过 20,000 个项目。 经过一些测试,我得出以下解决方案,可能会解决您的问题:

  • EF 对于海量数据导入速度非常慢
  • 所以我使用SqlBulkCopy-Command 将数据导入到一个额外的导入表中,并通过存储过程对数据库中的导入数据进行处理。导入数据的方法如下所示:

private void DoMassDataImport(DataTable tab)
 {            
    var conn = new SqlConnection(this.sqlConnectionString);
    try
    {
        if (conn.State != ConnectionState.Open)
        {
            conn.Open();
        }

        SqlTransaction transaction = conn.BeginTransaction();

        using (SqlBulkCopy copy = new SqlBulkCopy(conn, SqlBulkCopyOptions.Default, transaction))
        {
            copy.BulkCopyTimeout = 10000;
            copy.DestinationTableName = "dbo.TargetTable";
            copy.WriteToServer(tab);
        }
        transaction.Commit();
        result.Success = true;                
    }
    catch (Exception ex)
    {
        result.Success = false;
        this.LogEvent(ex.Message + Environment.NewLine + ex.StackTrace);
    }
    finally
    {
        conn.Close();                
    }
}
  • 可以从代码中调用存储过程,如果处理时间不超过 1 分钟,您可以使用 EF,但如果需要更长的时间,您应该考虑使用这样的普通 SQL 命令:

private void ProcessImportedData()
{
    SqlConnection connection = null;    
    try
    {
        connection = new SqlConnection(this.sqlConnectionString);
        if (connection.State != ConnectionState.Open)
        {
            connection.Open();
        }

        SqlTransaction transaction = connection.BeginTransaction();
        using (SqlCommand processingCommand = connection.CreateCommand())
        {
            processingCommand.Transaction = transaction;
            processingCommand.CommandTimeout = 10000;
            processingCommand.CommandText = "dbo.StoredProcedure ";
            processingCommand.CommandType = CommandType.StoredProcedure;
            processingCommand.ExecuteNonQuery();            
            result.Success = true
        }

        transaction.Commit();
    }
    catch (Exception ex)
    {
        result.Success = false;     
    }
    finally
    {
        if (connection != null)
        {
            connection.Close();

        }
    }
}
  • 使用这种方法将数据导入时间从超过 5 小时减少到不到 5 分钟

【讨论】:

  • 我第一次使用相同的想法,但是在传递数据时遇到了一些问题,主要是这两个表之间的PK。
  • @user2755456 这个方法的一个重点是我使用了一个额外的表(TargetTable),它只用于接收导入的数据,它与数据库中的任何其他表没有任何连接。执行“真实”导入和更新数据表的存储过程。所以导入有两个步骤,首先将文件中的数据放入一个数据库表中,然后处理导入的数据。
  • 没错,其实我是用这个方法的,但是我发现了一些我不知道的问题,了解一下会很有用:
  • 我同时更改了两张表,其中locations和sales,savechanges可以保存数据吗?
  • 这个片段: (_sales.Where(s => s.LocationId == record.Id && s.ReportDate == record.ReportDate)).Count() == 1 是为了查找如果只有一行与相同的特征匹配,但是,有没有其他方法可以做到这一点?
猜你喜欢
  • 1970-01-01
  • 2016-07-26
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 2022-01-04
  • 2011-07-21
  • 2018-04-14
  • 1970-01-01
相关资源
最近更新 更多