Я пытаюсь добавить оператор sql к методу миграции для моего текущего проекта. База данных представляет собой базу данных Ms Access. Миграции применяются во время выполнения. Ситуация следующая: у меня есть базовая Initial-create
миграция, которая в моем случае считается уже примененной. Из-за характера этого приложения у нас есть таблица A, которая содержит какой-то внешний ключ, но без каких-либо ограничений sql. Это означает, что отношение внешнего ключа разработано с помощью программного кода, а не в sql означает отношение внешнего ключа. Ключ представляет собой строку, и если нет внешнего элемента, значение пусто. Теперь мы хотим добавить новую миграцию, которая применяет эту связь через sql-ограничения. Это прекрасно работает с помощью стандартного кода миграции ef-core, но проблема возникает, когда миграция применяется к непустой базе данных. Для внешнего ключа sql все пустые строки в таблице A должны быть нулевыми (в противном случае мы получим исключение)
Казалось бы, простым решением было добавить следующий оператор в метод up новой миграции:
UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\""
Но это приводит к следующему исключению:
System.Data.OleDb.OleDbException (0x80040E14): The database engine could not lock table 'A' because it is already in use by another person or process.
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextErrorHandling(OleDbHResult hr)
at System.Data.OleDb.OleDbCommand.ExecuteCommandTextForSingleResult(tagDBPARAMS dbParams, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommandText(Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteCommand(CommandBehavior behavior, Object& executeResult)
at System.Data.OleDb.OleDbCommand.ExecuteReaderInternal(CommandBehavior behavior, String method)
at System.Data.OleDb.OleDbCommand.ExecuteNonQuery()
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQueryCore()
at EntityFrameworkCore.Jet.Data.JetCommand.<>c.<ExecuteNonQuery>b__40_0(Int32 _, JetCommand command)
at System.Linq.Enumerable.Aggregate[TSource,TAccumulate](IEnumerable`1 source, TAccumulate seed, Func`3 func)
at EntityFrameworkCore.Jet.Data.JetCommand.ExecuteNonQuery()
at Microsoft.EntityFrameworkCore.Storage.RelationalCommand.ExecuteNonQuery(RelationalCommandParameterObject parameterObject)
at Microsoft.EntityFrameworkCore.Migrations.MigrationCommand.ExecuteNonQuery(IRelationalConnection connection, IReadOnlyDictionary`2 parameterValues)
at Microsoft.EntityFrameworkCore.Migrations.Internal.MigrationCommandExecutor.ExecuteNonQuery(IEnumerable`1 migrationCommands, IRelationalConnection connection)
at Microsoft.EntityFrameworkCore.Migrations.Internal.Migrator.Migrate(String targetMigration)
at Microsoft.EntityFrameworkCore.RelationalDatabaseFacadeExtensions.Migrate(DatabaseFacade databaseFacade)
at X.Infrastructure.Setup.Migrate(IFactory`1 pDatabaseContextFactory, String pDatabasePath)
at X.COM.XCOMWrapper.Setup(ISettingsProvider pSettingsProvider)
Однако, если мы удалим этот оператор sql из кода миграции и выполним его следующим образом, перед вызовом context.Database.Migrate()
:
var dbConnection = context.Database.GetDbConnection();
dbConnection.Open();
using (var transaction = dbConnection.BeginTransaction())
{
var updateForeignKeyReferences= dbConnection.CreateCommand();
updateForeignKeyReferences.CommandText = "UPDATE A SET ForeignKeyColumn = NULL WHERE ForeignKeyColumn & \"\" = \"\"";
updateForeignKeyReferences.ExecuteNonQuery();
transaction.Commit();
}
dbConnection.Close();
Это работает просто отлично. Является ли мой подход к использованию кода sql в методе up совершенно неправильным? Каковы возможные причины этого? И самое главное, как я могу это исправить? Второй подход — это мой текущий обходной путь для этой проблемы, но я боюсь, что это означает, что в долгосрочной перспективе я не смогу использовать механизм миграции и должен использовать собственное решение (или другую структуру). Я бы предпочел просто придерживаться ef core.
Важно: это приложение работает с устаревшим приложением, и мы должны вставить историю приложения с помощью кода sql при первоначальном запуске. Для этого мы создаем транзакцию и просто создаем таблицу истории и вставляем изначально созданную таблицу. Это работает просто отлично, и транзакции, а также команды должны быть закрыты. Эта функция никогда не затрагивает таблицу A.