Ошибка при вставке записи в базу данных SQL Server из веб-API ASP .Net Core 2.1 с использованием EF Core 2.1

Я создал новый веб-API ASP.Net Core 2.1. Он использует EF Core, сначала код, для чтения и записи в базу данных SQL Server. Поэтому я использовал миграцию для создания / создания базы данных.

На метод действия [HTTPPOST] в контроллере, когда он добавляет новую запись в DbContext и пытается сохранить, я получаю следующую ошибку:

System.Data.SqlClient.SqlException (0x80131904): Невозможно вставить явное значение для столбца идентификатора в таблице "Показания", если для параметра IDENTITY_INSERT установлено значение OFF.

База данных имеет только одну таблицу:

USE [eballcoz_mssql]
GO

/****** Object:  Table [eballcoz_admin].[Readings]    Script Date: 2018/11/08 21:13:34 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [eballcoz_admin].[Readings](
    [Id] [int] IDENTITY(1,1) NOT NULL,
    [BaseId] [int] NULL,
    [Frequency] [int] NULL,
    [Modulation] [int] NULL,
    [Agc1] [int] NULL,
    [Agc2] [int] NULL,
    [TimeStamp] [datetime2](7) NULL,
 CONSTRAINT [PK_Readings] PRIMARY KEY CLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO

Моя модель выглядит так:

public class Reading
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public int? BaseId { get; set; }
    public int? Frequency { get; set; }
    public int? Modulation { get; set; }
    public int? Agc1 { get; set; }
    public int? Agc2 { get; set; }
    public DateTime? TimeStamp { get; set; }
}

И мой метод действия, как это:

    // POST: api/Readings/one
    [HttpPost]
    public async Task<IActionResult> PostReading([FromBody] Reading reading)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        _context.Readings.Add(reading);
        await _context.SaveChangesAsync();

        return CreatedAtAction("GetReading", new { id = reading.Id }, reading);
    }

Я понимаю, в чем проблема - моя модель включает поле первичного ключа "Id", и поэтому она пытается записать это в таблицу базы данных, что SQL Server не нравится. Проблема в том, что мне нужно поле "Id" в модели, когда я читаю из базы данных. Я бы подумал, что [DatabaseGenerated(DatabaseGeneratedOption.Identity)] декоратор в модели скажет EF, что он не должен пытаться вставить столбец Id, но, похоже, он не работает. Я также попытался сделать это в FLUENT:

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Reading>()
        .HasKey(r => r.Id);

        modelBuilder.Entity<Reading>()
        .Property(r => r.Id)
        .UseSqlServerIdentityColumn()
        .ValueGeneratedOnAdd();
    }

но безрезультатно. Как я могу сохранить столбец Id как часть моей модели, но сказать EF не включать его в запрос INSERT? Я прочитал это:

Есть два основных способа вставки записей без ошибок: 1) Когда IDENTITY_INSERT установлен в OFF. ПЕРВИЧНЫЙ КЛЮЧ "ID" НЕ ДОЛЖЕН БЫТЬ НАСТОЯЩИМ 2) Когда IDENTITY_INSERT установлен в ON. ПЕРВИЧНЫЙ КЛЮЧ "ИД" ДОЛЖЕН БЫТЬ НАСТОЯЩИМ

Поэтому я в основном пытаюсь найти решение № 1 - я не хочу указывать значение первичного ключа. Я хочу, чтобы SQL Server автоматически генерировал его для меня. Но моя модель действительно включает столбец Id, потому что он мне нужен при чтении из базы данных... Есть идеи?

2 ответа

Решение

Вы должны проверить при чтении. Ид. Это должно быть 0, когда вы добавляете объект в ваш dbcontext. Вы можете отредактировать его, чтобы заставить: reading.Id = 0;

Entity Framework уже решает проблему, которую вы смотрите. Я считаю, что ваша проблема лежит в другом месте.

Поведение EF по умолчанию

Когда вы создаете новый объект со свойством int, значение по умолчанию равно 0. Это не обнуляемый тип, поэтому он не может быть null, Когда это свойство (Id в этом случае) помечается как первичный ключ с автоинкрементом, EF Core оставляет его как 0 в вашем DbContext до тех пор SaveChanges() называется, и EF Core затем заполняет Id свойство с любым значением, сгенерированным для него SQL Server.

var reading = new Reading(); //Id = 0
_context.Add(reading); //Id still 0
_context.SaveChanges(); //Id = 5 (or whatever Id from SQL
System.Console.Writeline($"Id: {reading.Id}" //output -> Id: 5

Настоящая проблема?

Когда вы получите ваш Reading объект от клиента (все, что публикуется в PostReading действие) Я собираюсь предположить, что Id поле уже заполнено на данный момент, вызывая вашу ошибку.

[HttpPost]
public async Task<IActionResult> PostReading([FromBody] Reading reading)
{
    if (!ModelState.IsValid)
    {
        return BadRequest(ModelState);
    }

    //quick debug test
    if (reading.Id > 0) 
    {
       throw new ArgumentException("Something erroneously filled out the Id.");
    }

    _context.Readings.Add(reading);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetReading", new { id = reading.Id }, reading);
}
Другие вопросы по тегам