Это известная проблема с Code First для существующей базы данных рабочий процесс, объясненный в Code First Migrations с существующей базой данных - Что нужно знать о разделе EF6 документация:
Имена по умолчанию / рассчитанные имена могут не соответствовать существующей схеме
Миграции явно задают имена для столбцов и таблиц, когда он переносит миграции. Однако существуют другие объекты базы данных, для которых Migrations вычисляет имя по умолчанию при применении миграций. Это включает в себя индексы и ограничения внешнего ключа. При нацеливании на существующую схему эти вычисленные имена могут не соответствовать тому, что фактически существует в вашей базе данных.
и предлагаемое решение - вручную отредактировать сгенерированный код миграции и использовать необязательный аргумент name
(как указано в другом ответе):
Если для будущих изменений в вашей модели потребуется изменить или отбросить один из объектов базы данных с другим именем, вам нужно будет изменить миграцию в скаффолде, чтобы указать правильное имя. В API миграции есть необязательный параметр Name, который позволяет вам это делать. Например, ваша существующая схема может иметь таблицу Post со столбцом внешнего ключа BlogId с индексом IndexFk_BlogId. Тем не менее, по умолчанию Миграции ожидают, что этот индекс будет называться IX_BlogId. Если вы внесете в свою модель изменения, которые приведут к удалению этого индекса, вам нужно будет изменить вызов DropIndex для скаффолдинга, чтобы указать имя IndexFk_BlogId.
Конечно, никто не хотел бы делать это вручную. К сожалению, как я уже упоминал в своем ответе на соглашение об уникальных индексах в EF6 , проблема с именами ограничений PK и FK заключается в том, что в EF6 нет элемента / свойства / аннотации метаданных для управления ими. Если бы был такой способ, скорее всего, обратный инженер процесс использовал бы его. Но чтобы быть на сто процентов уверенным, я проверил исходный код, и хотя оба параметра ForeignKeyOperation
и PrimaryKeyOperation
имеют настраиваемое свойство Name
, оно не указывается никакой другой операцией, кроме вызовов миграции из скаффолдинга.
Вскоре идея конвенции мертва. Что еще можно сделать? Ну, хотя мы не можем контролировать это с помощью метаданных, к счастью, мы можем контролировать код миграции генерация через пользовательский MigrationCodeGenerator класс:
Базовый класс для провайдеров, которые генерируют код для миграции на основе кода.
Так как это C #, мы унаследуем CSharpMigrationCodeGenerator , переопределим метод Generate , применим наше соглашение об именах к каждому ForeignKeyOperation
и PrimaryKeyOperation
и пусть база сделает все остальное , Пример реализации может быть таким:
using System;
using System.Collections.Generic;
using System.Data.Entity.Migrations.Design;
using System.Data.Entity.Migrations.Model;
using System.Data.Entity.Migrations.Utilities;
using System.Linq;
class CustomMigrationCodeGenerator : CSharpMigrationCodeGenerator
{
public override ScaffoldedMigration Generate(string migrationId, IEnumerable<MigrationOperation> operations, string sourceModel, string targetModel, string @namespace, string className)
{
foreach (var fkOperation in operations.OfType<ForeignKeyOperation>()
.Where(op => op.HasDefaultName))
{
fkOperation.Name = fkOperation.Name.Replace("dbo.", "");
// or generate FK name using DependentTable, PrincipalTable and DependentColumns properties,
// removing schema from table names if needed
}
foreach (var pkOperation in operations.OfType<PrimaryKeyOperation>()
.Concat(operations.OfType<CreateTableOperation>().Select(op => op.PrimaryKey))
.Where(op => op.HasDefaultName))
{
pkOperation.Name = pkOperation.Name.Replace("dbo.", "");
// or generate PK name using Table and Columns properties,
// removing schema from table name if needed
}
return base.Generate(migrationId, operations, sourceModel, targetModel, @namespace, className);
}
protected override void GenerateInline(AddForeignKeyOperation addForeignKeyOperation, IndentedTextWriter writer)
{
writer.WriteLine();
writer.Write(".ForeignKey(" + Quote(addForeignKeyOperation.PrincipalTable) + ", ");
Generate(addForeignKeyOperation.DependentColumns, writer);
if (addForeignKeyOperation.CascadeDelete)
writer.Write(", cascadeDelete: true");
// { missing in base implementation
if (!addForeignKeyOperation.HasDefaultName)
{
writer.Write(", name: ");
writer.Write(Quote(addForeignKeyOperation.Name));
}
// }
writer.Write(")");
}
}
Обратите внимание, что нам также необходимо переопределить (заменить) базовую реализацию метода GenerateInline(AddForeignKeyOperation
(который используется, когда FK создается как часть операции создания таблицы), поскольку в настоящее время в нем есть ошибка, которая игнорирует Name
свойство (см. комментарии в коде).
После того, как вы это сделаете, все, что вам нужно, это заменить стандартный генератор кода миграции, установив свойство CodeGenerator внутри вашего DbMigrationsConfiguration
конструктора производного класса:
internal sealed class Configuration : DbMigrationsConfiguration<MyDbContext>
{
public Configuration()
{
CodeGenerator = new CustomMigrationCodeGenerator();
// ...
}
}