Массовая вставка с EntityFramework 4.0 вызывает прерывание транзакции - PullRequest
1 голос
/ 02 мая 2011

Мы получаем файл от клиента (Silverlight) через WCF, и на стороне сервера я анализирую этот файл. Каждая строка в файле преобразуется в объект и сохраняется в базе данных. если файл очень большой (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;
        }
    }

Ответы [ 3 ]

7 голосов
/ 02 мая 2011

В структуре сущностей нет массовой вставки . Вы вызываете SaveChanges после 100 записей, но он выполнит 100 отдельных вставок с циклом передачи базы данных для каждой вставки.

Установка времени ожидания транзакции также зависит от транзакции max timeout , настроенной на уровне компьютера (я думаю, что значение по умолчанию составляет 10 минут). Сколько времени нужно, чтобы ваша операция не сработала?

Лучший способ сделать это - переписать логику вставки с помощью обычного ADO.NET или массовой вставки.

Btw. throw exception и throw e? Это неверный способ перебрасывать исключения .

Важное редактирование:

SaveChanges(SaveOptions.None) !!! означает, что не принимать изменения после сохранения, поэтому все записи все еще находятся в добавленном состоянии. Из-за этого при первом вызове SaveChanges будут вставлены первые 100 записей. Второй вызов снова вставит первые 100 + следующие 100, третий вызов вставит первые 200 + следующие 100 и т. Д.

1 голос
/ 23 марта 2012

У меня была точно такая же проблема. Я сделал 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;
    }
0 голосов
/ 07 мая 2011

Время ожидания истекло из-за максимального времени ожидания TransactionScope по умолчанию, проверьте значение machine.config.

Проверьте эту ссылку:

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...