【问题标题】:Bulk inserts with EntityFramework 4.0 causes abort of transaction使用 EntityFramework 4.0 进行批量插入会导致事务中止
【发布时间】:2012-10-28 22:56:20
【问题描述】:

我们正在通过 WCF 从客户端 (Silverlight) 接收文件,并在服务器端解析该文件。文件中的每一行都被转换为一个对象并存储到数据库中。如果文件非常大(超过 10000 个条目),我会收到以下错误(MSSQLEXPRESS):

与当前连接关联的事务已完成但尚未处理。必须先处理事务,然后才能使用连接执行 SQL 语句。

我尝试了很多(TransactionOptions 超时设置等),但没有任何效果。上述异常消息要么在 3000 之后引发,有时在 6000 个对象处理之后引发,但我无法成功处理所有对象。

我附上了我的来源,希望有人有想法并可以帮助我:

public xxxResponse SendLogFile (xxxRequest request
{
   const int INTERMEDIATE_SAVE = 100;



   using (var context = new EntityFramework.Models.Cubes_ServicesEntities())
   {
            // start a new transactionscope with the timeout of 0 (unlimited time for developing purposes)
            using (var transactionScope = new TransactionScope(TransactionScopeOption.RequiresNew,
            new TransactionOptions
            {
                IsolationLevel = System.Transactions.IsolationLevel.Serializable,
                Timeout = TimeSpan.FromSeconds(0)
            }))
            {
                try
                {
                    // open the connection manually to prevent undesired close of DB
                    // (MSDTC)
                    context.Connection.Open();
                    int timeout = context.Connection.ConnectionTimeout;

                    int Counter = 0;

                    // read the file submitted from client
                    using (var reader = new StreamReader(new MemoryStream(request.LogFile)))
                    {
                        try
                        {
                            while (!reader.EndOfStream)
                            {
                                Counter++;
                                Counter2++;
                                string line = reader.ReadLine();
                                if (String.IsNullOrEmpty(line)) continue;

                                // Create a new object
                                DomainModel.LogEntry le = CreateLogEntryObject(line);

                                // an attach it to the context, set its state to added.
                                context.AttachTo("LogEntry", le);
                                context.ObjectStateManager.ChangeObjectState(le, EntityState.Added);

                                // while not 100 objects were attached, go on
                                if (Counter != INTERMEDIATE_SAVE) continue;

                                // after 100 objects, make a call to SaveChanges.
                                context.SaveChanges(SaveOptions.None);
                                Counter = 0;
                            }
                        }
                        catch (Exception exception)
                        {
                            // cleanup
                            reader.Close();
                            transactionScope.Dispose();
                            throw exception;

                        }

                    }
                    // do a final SaveChanges
                    context.SaveChanges();
                    transactionScope.Complete();
                    context.Connection.Close();
                }
                catch (Exception e)
                {
                    // cleanup
                    transactionScope.Dispose();
                    context.Connection.Close();
                    throw e;
                }
            }

            var response = CreateSuccessResponse<ServiceSendLogEntryFileResponse>("SendLogEntryFile successful!");
            return response;
        }
    }

【问题讨论】:

  • 尝试重构该代码,使其更具可读性。没有人喜欢在你发布的混乱中徘徊。这是一本可以帮助您创建更简洁代码的好书:amzn.to/1G1pk。看看这个视频:bit.ly/irhPnl.

标签: entity-framework transactions


【解决方案1】:

实体框架中有no bulk insert。您在 100 条记录后调用 SaveChanges,但它会执行 100 次单独的插入,每次插入都需要数据库往返。

设置事务的超时时间还取决于在机器级别配置的事务max timeout(我认为默认值为 10 分钟)。在您的操作失败之前需要多长时间?

您可以做的最好的方法是使用通用 ADO.NET 或批量插入重写您的插入逻辑。

顺便说一句。 throw exceptionthrow e?这是rethrow exceptions 的错误方式。

重要修改:

SaveChanges(SaveOptions.None) !!!表示保存后不接受更改,因此所有记录仍处于添加状态。因此,第一次调用 SaveChanges 将插入前 100 条记录。第二次调用将再次插入前 100 + 下一个 100,第三次调用将插入前 200 + 下一个 100,依此类推。

【讨论】:

  • 您好,感谢您的提示,默认值为 10 分钟 - 我会尝试检查一下。重新抛出 e 的目的是因为在源代码中(您看不到这一点),异常将被捕获并映射到具有错误描述等的有效响应对象中。
  • 我也会给标准 ADO 一个机会和 UPPSSS:你是对的 savechanges() 行为!!!谢谢!!!
  • 啊,现在我明白你所说的“不精确的重新抛出方式......”是什么意思
【解决方案2】:

我遇到了完全相同的问题。我做了 EF 代码,每次插入批量 1000 条记录。

我从一开始就在工作,msDTC 有一点问题,我把它允许远程客户端和管理员,但之后就没事了。我为此做了很多工作,但有一天它就停止工作了。

我得到了

与当前连接关联的事务已完成但尚未处理。必须先释放事务,然后才能使用连接执行 SQL 语句。

很奇怪!有时错误会发生变化。我的嫌疑人不知何故是 msDTC,奇怪的行为。 我现在正在改变,因为不使用 TransactionScope!

我讨厌它确实起作用然后停下来。我还尝试在虚拟机中运行它,这又是一次巨大的时间浪费...... 我的代码:

    private void AddTicks(FileHelperTick[] fhTicks)
    {
        List<ForexEF.Entities.Tick> Ticks = new List<ForexEF.Entities.Tick>();

        var str = LeTicks(ref fhTicks, ref Ticks);

        using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions()
        {
            IsolationLevel = System.Transactions.IsolationLevel.Serializable,
            Timeout = TimeSpan.FromSeconds(180)
        }))
        {
            ForexEF.EUR_TICKSContext contexto = null;                
            try
            {
                contexto = new ForexEF.EUR_TICKSContext();

                contexto.Configuration.AutoDetectChangesEnabled = false;

                int count = 0;
                foreach (var tick in Ticks)
                {
                    count++;
                    contexto = AddToContext(contexto, tick, count, 1000, true);
                }
                contexto.SaveChanges();
            }
            finally
            {
                if (contexto != null)
                    contexto.Dispose();
            }
            scope.Complete();
        }   
    }

    private ForexEF.EUR_TICKSContext AddToContext(ForexEF.EUR_TICKSContext contexto, ForexEF.Entities.Tick tick, int count, int commitCount, bool recreateContext)
    {
        contexto.Set<ForexEF.Entities.Tick>().Add(tick);

        if (count % commitCount == 0)
        {
            contexto.SaveChanges();
            if (recreateContext)
            {
                contexto.Dispose();
                contexto = new ForexEF.EUR_TICKSContext();
                contexto.Configuration.AutoDetectChangesEnabled = false;
            }
        }

        return contexto;
    }

【讨论】:

    【解决方案3】:

    由于 TransactionScope 默认最大超时而超时,请检查 machine.config 以了解该情况。

    查看此链接:

    http://social.msdn.microsoft.com/Forums/en-US/windowstransactionsprogramming/thread/584b8e81-f375-4c76-8cf0-a5310455a394/

    【讨论】:

      猜你喜欢
      • 2015-06-17
      • 1970-01-01
      • 2019-03-10
      • 2017-08-11
      • 2017-08-23
      • 1970-01-01
      • 2015-11-23
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多