Entity Framework не учитывает мои столбцы Identity. Он настаивает на попытке вставить значение в столбец Identity (автоинкремент) в моей БД MS SQL, что, очевидно, является ошибкой, поскольку БД должна предоставлять значение.
System.Data.SqlClient.SqlException: 'Cannot insert explicit value for identity column in table 'Assignee' when IDENTITY_INSERT is set to OFF.'
Почему он пытается это сделать? Я соединил его со схемой, включающей одну таблицу и один столбец:
CREATE TABLE [dbo].[Assignee](
[AssigneeID] INT IDENTITY(-1, 1) NOT NULL
CONSTRAINT [Assignee$PrimaryKey] PRIMARY KEY CLUSTERED
( [AssigneeID] ASC ))
После публикации этой схемы в моей локальной БД я использую Scaffold-DbContext для создания классов сущностей и контекста. Сгенерированный класс Assignee содержит только это общедоступное свойство.
public int AssigneeId { get; set; }
Контекст относится только к Assignee здесь:
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
Поискав вокруг, я вижу людей, утверждающих, что для того, чтобы EF учитывал столбцы Identity, контекст должен настроить свойство с помощью ValueGeneratedOnAdd(). Другими словами, строка в классе контекста должна выглядеть так:
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID")
.ValueGeneratedOnAdd();
У меня есть две проблемы с этим:
ValueGeneratedOnAdd(), то почему Scaffold-DbContext не генерирует его?ValueGeneratedOnAdd() его еще не будет работать с той же ошибкой.В другом месте я вижу предложения по использованию UseSqlServerIdentityColumn(). Это тоже не работает для меня. Пункты 1 и 2 остаются в силе.
Любая помощь будет принята с благодарностью. Пожалуйста, не предлагайте мне использовать IDENTITY_INSERT, так как это лишает смысла использование столбцов с автоинкрементом.
(Я использую Entity Framework Core 2.2.3 и Microsoft SQL Server 14)
В столбце идентификаторов отсутствует аннотация данных DatabaseGeneratedOption.Identity/конфигурация API Fluent. Почему это не было создано эшафотом, я не могу сказать. Обратите внимание, что предложенная явная аннотация [Key] не является необходимой, хотя она должна работать, поскольку PK по умолчанию являются столбцами идентификаторов.
IDENTITY(-1, 1) (-1) опечатка?





Для БД сначала попробуйте добавить [ключ] в качестве аннотации к данным.
С аннотацией данных
[Key]
public int AssigneeId { get; set; }
свободный API
modelBuilder.Entity<Assignee>()
.HasKey(o => o.AssigneeId);
См. здесь или здесь, если вы хотите использовать свободный API.
Я создаю свои классы сущностей из существующей БД, используя скаффолд. Я действительно не должен редактировать эти классы. Кроме того, почему вы ожидаете, что это решит мою проблему?
Кроме того, я пробовал это как с аннотацией, так и с плавным API, и поведение осталось прежним.
Да, вы можете использовать частичные классы с MetadataTypeAttribute, см. здесь, например, stackoverflow.com/questions/6131754/…
Я попытался воспроизвести эту проблему на основе вашего примера, но, похоже, он работает нормально. Однако я не использовал Scaffold, просто закодировал класс, и я попробовал модель, создающую код, который у вас был, и у него не было проблем. Я подозреваю, что в этом должно быть что-то большее, потому что только с классом «Уполномоченный» соглашение EF ожидает таблицу «Уполномоченные», поэтому я подозреваю, что настраивается больше сопоставления.
Протестировано с EF Core 2.0.3 и 2.2.4.
БД: использовал сценарий ОП.
Сущность:
[Table("Assignee")]
public class Assignee
{
public int AssigneeId { get; set; }
}
Мне пришлось использовать атрибут Table для сопоставления с именем таблицы.
Контекст:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
согласно комментарию ОП.
Тестовое задание:
[Test]
public void TestIncrement()
{
using (var context = new TestDbContext())
{
var newItem = new Assignee();
context.Assignees.Add(newItem);
context.SaveChanges();
}
}
Работает как положено.
Однако то, что я обычно имею для объекта:
[Table("Assignee")]
public class Assignee
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity), Column("AssigneeID")]
public int AssigneeId { get; set; }
}
И тогда ничего для этого столбца не нужно в контексте переопределения OnModelCreating.
Я подозреваю, что где-то скрывается какая-то дополнительная конфигурация, поскольку нет упоминания о проблеме с именем таблицы, либо добавленной вручную, либо с помощью скаффолда, который искажает EF. Я полностью ожидал, что EF потерпит неудачу без атрибутов Key/DbGenerated, но, похоже, он работал нормально.
Обновлено: также пробовал это с помощью скафолдинга, запускающего Scaffold-DbContext по существующей схеме. Опять же, работал без проблем. Для сравнения с вашими тестами:
Сгенерированный DbContext: (без изменений сохранить удаление сведений о предупреждении и строке подключения.)
public partial class AssigneeContext : DbContext
{
public AssigneeContext()
{
}
public AssigneeContext(DbContextOptions<AssigneeContext> options)
: base(options)
{
}
public virtual DbSet<Assignee> Assignee { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
if (!optionsBuilder.IsConfigured)
{
optionsBuilder.UseSqlServer("Data Source=machine\\DEV;Initial Catalog=Spikes;uid=user;pwd=password;MultipleActiveResultSets=True");
}
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.HasAnnotation("ProductVersion", "2.2.4-servicing-10062");
modelBuilder.Entity<Assignee>(entity =>
{
entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID");
});
}
}
Сгенерированный объект: (без изменений)
public partial class Assignee
{
public int AssigneeId { get; set; }
}
Я понял, зачем нужна моя аннотация таблицы. EF Core (не уверен, что применимо и к EF6) основывал соглашение об имени таблицы на имени переменной DbSet в DbContext. Я не видел никаких различий в конфигурации с контекстом, сгенерированным скаффолдом, и моим собственным, кроме имени DbSet. Я переименовал свое оригинальное имя DbSet DbContext в «Assignee», и оно работало без атрибута Table.
Тем не менее, на основе представленной информации ваш код должен работать. Что-то скрывается в деталях, потому что этот пример действительно работает, поэтому вам нужно будет предоставить более подробную информацию о примере, который определенно не работает в вашем случае.
Это довольно подробный пример. К сожалению, у меня есть большая существующая БД, и мне приходится использовать скаффолд. :-( Очень странно, что вашему коду нужна аннотация таблицы, а моему нет. Я понимаю, что вы имеете в виду под «дополнительной конфигурацией», но я не знаю, что/где это будет. Скаффолд AFIK сбрасывает весь сгенерированный код в папку по вашему выбору, и единственное, что я вижу, это мой контекст и классы Assignee.
Можете ли вы разместить определение DbContext? Constructor, OnModelCreating и т. д. Существуют ли какие-либо классы, расширяющие IEntityTypeConfiguration? EF (по-прежнему) имеет 4 способа настройки сущностей: атрибуты, соглашение (автоматически), IEntityTypeConfiguration и через modelBuilder в OnModelCreating, и любое их сочетание может использоваться в любом заданном сценарии. То, что ваше, кажется, разрешает имя таблицы по сравнению с соглашением по умолчанию без атрибута, может быть хорошим ключом к другому поведению.
Я повторно запустил тест, используя контекст и модель, построенные на каркасе, и он все еще работал, как и ожидалось. Обновил ответ с подробностями.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Todo>(entity =>
{
entity.Property(x => x.Id)
.HasColumnName("id")
.HasColumnType("int")
.ValueGeneratedOnAdd()
**.UseIdentityColumn();**
}
Попробуйте сделать это. Зависимость ядра Ef: Microsoft.EntityFrameworkCore.SqlServer
Укороченная версия
Мы получаем и испытываем разные результаты: одни могут воспроизвести проблему, другие — нет. Мой опыт зависит от того, равно ли значение свойства Id 0 или нет.
Подробная версия
По моему опыту, поведение по умолчанию (основанное на соглашении об именах) определенно работает, поэтому, если вы называете атрибут вашего объекта db (свойство C#) идентификатором или EntityNameId, он должен работать. Нет атрибутов класса сущностей C#, ни конфигурация OnModelCreating не требуется. В то же время, если проблема существует, ни атрибуты класса сущностей C#, ни конфигурация OnModelCreating не исправят ее.
...поскольку если значение свойства Id не равно 0, сгенерированный SQL будет содержать явное имя и значение поля, поэтому мы получили ошибку. Это явно проблема в ядре EF, но обходной путь прост.
Это оказывается правильным ответом. Он специально обрабатывает значение по умолчанию для типа идентификатора (0). Если значение равно 0, оно будет сгенерировано. Если значение не равно 0, предполагается, что вы его установили, попытаетесь вставить и потерпите неудачу. Я бы предположил, что это не проблема, а сомнительная "фича".
Это работает для меня:
modelBuilder.Entity<Assignee>().Property(e => e.AssigneeId).UseIdentityColumn();
Итак, UseIdentityColumn() является ключом.
Я использую Microsoft.EntityFrameworkCore.SqlServer v3.1.8.
Как уже ответил здесь.
Модель и база данных EF Core кажутся правильными. Сообщение об исключении указывает, что ваш код добавляет
Assigneeс явно указаннымAssigneeId(кроме0), и в этом случае EF Core учитывает ваше явное значение (это необходимо для поддержки сценариев вставки удостоверений). Перед вызовом методаAssigneeIdубедитесь, чтоAddравно нулю.