Ошибка первой миграции кода EF "Объект отключен или не существует на сервере"

Я использую Entity Framework 6.1.1 на SQL Server 2008, и у меня есть долго работающий код первой миграции (около 20 минут). Доходит до конца, а затем выдает следующую ошибку.

System.Runtime.Remoting.RemotingException: Object '/f10901d8_94fe_4db4_bb9d_51cd19292b01/bq6vk4vkuz5tkri2x8nwhsln_106.rem' has been disconnected or does not exist at the server.
   at System.Data.Entity.Migrations.Design.ToolingFacade.ToolLogger.Verbose(String sql)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ExecuteSql(DbTransaction transaction, MigrationStatement migrationStatement, DbInterceptionContext interceptionContext)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbTransaction transaction, DbInterceptionContext interceptionContext)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteStatementsInternal(IEnumerable`1 migrationStatements, DbConnection connection)
   at System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClass30.<ExecuteStatements>b__2e()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.<>c__DisplayClass1.<Execute>b__0()
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute[TResult](Func`1 operation)
   at System.Data.Entity.SqlServer.DefaultSqlExecutionStrategy.Execute(Action operation)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements, DbTransaction existingTransaction)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteStatements(IEnumerable`1 migrationStatements)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.ExecuteStatements(IEnumerable`1 migrationStatements)
   at System.Data.Entity.Migrations.DbMigrator.ExecuteOperations(String migrationId, XDocument targetModel, IEnumerable`1 operations, IEnumerable`1 systemOperations, Boolean downgrading, Boolean auto)
   at System.Data.Entity.Migrations.DbMigrator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.ApplyMigration(DbMigration migration, DbMigration lastMigration)
   at System.Data.Entity.Migrations.DbMigrator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.Infrastructure.MigratorLoggingDecorator.Upgrade(IEnumerable`1 pendingMigrations, String targetMigrationId, String lastMigrationId)
   at System.Data.Entity.Migrations.DbMigrator.UpdateInternal(String targetMigration)
   at System.Data.Entity.Migrations.DbMigrator.<>c__DisplayClassc.<Update>b__b()
   at System.Data.Entity.Migrations.DbMigrator.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.EnsureDatabaseExists(Action mustSucceedToKeepDatabase)
   at System.Data.Entity.Migrations.DbMigrator.Update(String targetMigration)
   at System.Data.Entity.Migrations.Infrastructure.MigratorBase.Update(String targetMigration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.UpdateRunner.Run()
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.AppDomain.DoCallBack(CrossAppDomainDelegate callBackDelegate)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Run(BaseRunner runner)
   at System.Data.Entity.Migrations.Design.ToolingFacade.Update(String targetMigration, Boolean force)
   at System.Data.Entity.Migrations.UpdateDatabaseCommand.<>c__DisplayClass2.<.ctor>b__0()
   at System.Data.Entity.Migrations.MigrationsDomainCommand.Execute(Action command)

Смысл миграции заключается в обновлении поля в базе данных, в котором хранится MIME-тип некоторых двоичных данных. Он просматривает каждую строку, читает двоичные данные, пытается определить, какой это тип содержимого, а затем записывает соответствующее значение типа MIME в эту строку.

Приведенный ниже скрипт использует ADO.NET для генерации списка операторов обновления для запуска. Я использую ADO.NET, потому что я должен использовать библиотеки изображений.NET (System.Drawing.Imaging.ImageFormat), чтобы определить тип двоичного содержимого в каждой строке (это будет jpeg, png или pdf).

public override void Up()
{
    List<string> updateStatements = new List<string>();

    using(SqlConnection conn = new SqlConnection(ConfigurationManager.AppSettings["ConnectionString"]))
    {
        SqlCommand cmd = new SqlCommand("SELECT Table1ID, Image FROM Table1"), conn);
        conn.Open();

        //read each record and update the content type value based on the type of data stored
        using (SqlDataReader reader = cmd.ExecuteReader())
        {
            while (reader.Read())
            {
                long idValue = Convert.ToInt64(reader["Table1ID"]);
                byte[] data = (byte[])reader["Image"];
                string contentType = GetMimeType(data);
                updateStatements.Add(string.Format("UPDATE Table1 SET Content_Type = {0} WHERE Table1ID = {1}", contentType, idValue));
            }
        }
    }

    foreach (string updateStatement in updateStatements)
        Sql(updateStatement);
}

public string GetMimeType(byte[] document)
{
    if (document != null && document.Length > 0)
    {
        ImageFormat format = null;

        try
        {
            MemoryStream ms = new MemoryStream(document);
            Image img = Image.FromStream(ms);
            format = img.RawFormat;
        }
        catch (Exception)
        {
            /* PDF documents will throw exceptions since they aren't images but you can check if it's really a PDF
             * by inspecting the first four bytes with will be 0x25 0x50 0x44 0x46 ("%PDF"). */
            if (document[0] == 0x25 && document[1] == 0x50 && document[2] == 0x44 && document[3] == 0x46)
                return PDF;
            else
                return NULL;
        }

        if (format.Equals(ImageFormat.Jpeg))
        {
            return JPG;
        }
        else if (format.Equals(System.Drawing.Imaging.ImageFormat.Png))
        {
            return PNG;
        }
    }

    return NULL;
}

Я видел этот пятилетний пост и статьи, на которые он ссылается, похоже, больше не существует. По крайней мере, я не могу их найти.

Кто-нибудь знает, что здесь происходит?

-- ОБНОВИТЬ --
Похоже, это связано с тем, сколько времени занимает миграция. Я создал миграцию, которая не делает ничего кроме сна в течение 22 минут

public override void Up()
{
    System.Threading.Thread.Sleep(1320000);
}

и я получил ту же ошибку. Так что это похоже на тайм-аут. Я не на 100% на какой объект на сервере они ссылаются, и я не могу найти много по этой проблеме, так как это относится к первой миграции кода.

Я пытался установить CommandTimeout собственность в миграциях Configuration.cs файл до 5000, но это не помогло. Я также попытался установить SQL Server Remove query timeout установка 0, чтобы предотвратить любые тайм-ауты, но это тоже не помогло.

3 ответа

Извлечено из [GitHub EntityFramework 6, выпуск № 96] [ https://github.com/aspnet/EntityFramework6/issues/96

Проблема заключается в том, что срок аренды ToolLogger (базовый класс MigrationsLogger - это MarshalByRefObject) по умолчанию (5 минут). ToolingFacade создает регистратор, который находится в домене приложения основной программы. Миграции выполняются в другом домене приложения. Если миграция занимает более 5 минут, попытка записи любой дополнительной информации приводит к этой ошибке. Решение было бы увеличить срок аренды в основной программе. Итак... в основной программе перед созданием ToolingFacade установите срок аренды более длительным:

using System.Runtime.Remoting.Lifetime;
...
LifetimeServices.LeaseTime = TimeSpan.FromHours(1);

Это известная проблема в Entity Framework 6 для сценариев, выполнение которых занимает много времени.

Обходной путь - генерировать только SQL-скрипт через Update-Database введите команду и выполните сгенерированный SQL-запрос непосредственно на SQL-сервере. Чтобы генерировать только SQL, вы должны использовать -Script флаг:

Update-Database -Script

Это вызывает у нас головную боль. Похоже, проблема связана с дизайном утилиты миграции EF. Программа создает новый домен приложений, в котором запускаются миграции. Регистрация нового AppDomain ведется в исходном AppDomain (именно поэтому удаленное взаимодействие включается). Очевидно, что регистратор получает GC, если отдельная миграция занимает слишком много времени. Я подтвердил это, заменив все вызовы логгера на Console.WriteLine - что устраняет проблему. Это может быть исправлено путем изменения инструмента migrate.exe (но может потребоваться изменение самой сборки EntityFramework).

У меня возникла эта проблема, потому что строка подключения в моем файле web.config указывала на неправильное имя сервера. На самом деле мне пришлось изменить мой файл c:\windows\system32\drivers\etc\host, чтобы указать правильный IP-адрес для этого имени хоста БД. Просто убедитесь, что ваш сервер БД доступен.

Другие вопросы по тегам