Entity Framework Core не учитывает столбцы идентификаторов - PullRequest
2 голосов
/ 17 апреля 2019

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");
});

Осматривая, я вижу людей, утверждающих, что для того, чтобы E.F. уважал столбцы Identity, контекст должен сконфигурировать свойство с ValueGeneratedOnAdd(). Другими словами, строка в классе контекста должна выглядеть следующим образом:

entity.Property(e => e.AssigneeId).HasColumnName("AssigneeID")
  .ValueGeneratedOnAdd();

У меня есть две проблемы с этим:

  1. Я начинаю с существующей БД и генерирую классы сущностей. Если мне нужно ValueGeneratedOnAdd(), то почему Scaffold-DbContext не генерирует его?
  2. Даже если я вручную отредактирую сгенерированный класс контекста и добавлю ValueGeneratedOnAdd(), он все равно не будет работать с той же ошибкой.

В других местах я вижу предложения по использованию UseSqlServerIdentityColumn(). Это также не работает для меня. Пункты 1 и 2 остаются в силе.

Любая помощь будет очень признательна. Пожалуйста, не предлагайте мне использовать IDENTITY_INSERT, поскольку это лишает смысла использование столбцов с автоинкрементом.

(я использую Entity Framework Core 2.2.3 и Microsoft SQL Server 14)

Ответы [ 2 ]

0 голосов
/ 17 апреля 2019

Я пытался воспроизвести эту проблему на основе вашего примера, но, похоже, она работает нормально.Хотя я не использовал Scaffold, просто закодировал класс, и я попробовал модель, создающую код, который у вас был, и у него не было проблем.Я подозреваю, что в этом должно быть что-то большее, потому что только с классом «Assignee» соглашение EF ожидает таблицу «Assignees», поэтому я подозреваю, что настраивается больше сопоставлений.

Протестировано с EF Core2.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");
        });
    }

согласно комментарию OP.

Тест:

   [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 (не уверен, применимо ли это и к EF6) основывало соглашение для имени таблицы на имя переменной DbSet в DbContext.Я не увидел никакой разницы в конфигурации с контекстом, сгенерированным скаффолдом, и моим собственным, кроме имени DbSet.Я переименовал свое исходное имя DbSetxt в DbContext в «Assignee», и оно работало без атрибута Table.

Тем не менее, основываясь на представленной информации, ваш код должен работать.Что-то скрывается в деталях, потому что этот пример работает, поэтому вам нужно будет предоставить более подробную информацию о примере, который определенно не работает в вашем случае.

0 голосов
/ 17 апреля 2019

Для БД сначала попробуйте добавить [ключ] в качестве аннотации данных

С аннотацией данных

[Key]
public int AssigneeId { get; set; }

свободно API

modelBuilder.Entity<Assignee>()
        .HasKey(o => o.AssigneeId);

См. здесь или здесь , если вы хотите использовать свободный API

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...