Я использую Entity Framework Core с Oracle и настраиваю схему по умолчанию в своем контексте, используя HasDefaultSchema
со схемой PRODUCTMAX
. Создание индекса работает правильно, но когда я пытаюсь удалить индекс, команда, созданная в результате миграции, не включает схему, что приводит к ошибке.
В своем коде я использую migrationBuilder.DropIndex
с параметрами схемы, имени индекса и имени таблицы:
migrationBuilder.DropIndex(
name: "IX_ANALISENAOCONFORMIDADE_ANALISEID_TEXTO",
schema: "PRODUCTMAX",
table: "ANALISENAOCONFORMIDADE");
Я ожидаю, что это сгенерирует команду:
DROP INDEX PRODUCTMAX.IX_ANALISENAOCONFORMIDADE_ANALISEID_TEXTO
Но на самом деле при миграции создается:
DROP INDEX IX_ANALISENAOCONFORMIDADE_ANALISEID_TEXTO
Кто-нибудь знает, почему это происходит и как я могу это исправить, чтобы схема была правильно включена в команду DROP INDEX
?
Поскольку это давняя проблема с драйвером Oracle, вы можете пока использовать migrationBuilder.Sql("DROP INDEX PRODUCTMAX.IX_ANALISENAOCONFORMIDADE_ANALISEID_TEXTO")
. Исходный код драйвера следует пересмотреть, чтобы использовать Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name!, operation.Schema)
для генерации SQL, но сейчас он использует Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name!)
.
Предполагается, что вы используете поставщика баз данных Oracle.EntityFrameworkCore. Поставщики баз данных EF Core отвечают за реализацию специфики базы данных, и этот конкретный провайдер даже в последней на данный момент версии имеет ошибку в реализации метода службы, отвечающего за генерацию SQL для DropIndexOperation
(игнорирование свойства Schema
).
Лучше всего это исправить у провайдера. Исправить довольно просто, но почему-то мейнтейнеры уже давно этим не занимаются. К счастью, EF Core построен на архитектуре сервисов, в которой вы можете заменить каждую службу своей собственной, что позволяет улучшить/исправить/настроить их поведение.
Поэтому обходной путь, который я мог бы предложить, — это следовать типичному подходу, заключающемуся в замене некоторого класса реализации сервиса на собственный класс, унаследованный от оригинала, и переопределение одного или нескольких необходимых методов. В данном конкретном случае нам нужно заменить OracleMigrationsSqlGenerator
и переопределить Generate(DropIndexOperation operation...
метод.
Добавьте следующее в выбранный вами файл кода (например, CustomOracleServices.cs
) в проекте, где находится производный класс DbContext
:
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Migrations;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Oracle.EntityFrameworkCore.Infrastructure.Internal;
using Oracle.EntityFrameworkCore.Migrations;
namespace Microsoft.EntityFrameworkCore
{
public static class CustomOracleServices
{
public static DbContextOptionsBuilder UseCustomOracleServices(this DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder.ReplaceService<IMigrationsSqlGenerator, OracleMigrationsSqlGenerator, CustomOracleMigrationsSqlGenerator>();
}
}
namespace Microsoft.EntityFrameworkCore.Migrations
{
public class CustomOracleMigrationsSqlGenerator : OracleMigrationsSqlGenerator
{
#pragma warning disable EF1001 // Internal EF Core API usage.
public CustomOracleMigrationsSqlGenerator(MigrationsSqlGeneratorDependencies dependencies, IOracleOptions options) : base(dependencies, options) { }
#pragma warning restore EF1001 // Internal EF Core API usage.
protected override void Generate(DropIndexOperation operation, IModel? model, MigrationCommandListBuilder builder, bool terminate)
{
builder.Append("DROP INDEX ").Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(operation.Name, operation.Schema));
if (terminate) builder.AppendLine(Dependencies.SqlGenerationHelper.StatementTerminator).EndCommand();
}
}
}
Большая часть этого — это просто using
и сантехнический код, единственная важная часть — передача operation.Schema
в DelimitIdentifier
, которая отсутствует в исходном коде. На мой (и, думаю, на любой) вкус слишком много, но, по крайней мере, это позволяет вам исправить что-то, что в противном случае вы бы застряли.
В любом случае, последняя часть, необходимая для того, чтобы это заработало, — это переопределить метод OnConfiguring
вашего класса контекста базы данных и вызвать удобный метод UseCustomOracleServices
, который я добавил, например.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// ...
optionsBuilder.UseCustomOracleServices();
// ...
}
И все — проблема решена. Если кто-то жалуется на предупреждение «внутреннего API», все это означает, что при обновлении версии поставщика EF Core/db проверьте, есть ли какие-либо изменения во «внутреннем API», и ваш код все еще компилируется/функционирует. И, конечно же, если они исправят проблему в какой-то будущей версии, просто удалите весь специальный код, используемый для ее обхода.
Разве это не та же самая проблема, что и в вопросах и ответах?