Ошибка при вставке записи в базу данных 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);
}