我创建了一个新的ASP .Net Core 2.1 Web API。它使用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查询中?我读过这篇文章:
基本上有2种不同的方式来插入记录而不会出错: 1)当IDENTITY_INSERT设置为OFF时。必须不存在主键“ ID” 2)当IDENTITY_INSERT设置为ON时。必须存在主键“ ID”
所以我基本上是在尝试解决方案#1-我不想提供主键值。我希望SQL Server为我自动生成它。但是我的模型确实包含Id列,因为从数据库中读取时需要它...有什么想法吗?
答案 0 :(得分:1)
实体框架已经可以解决您要解决的问题。我相信您的问题出在其他地方。
新建属性为int
的对象时,其默认值为0。它不是可为null的类型,因此不能为null
。
当将该属性(在这种情况下为Id
标记为自动递增的主键时,EF Core在您的DbContext
中将其保留为0,直到调用SaveChanges()
,然后EF Core用SQL Server为其生成的任何值填充Id
属性。
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);
}
答案 1 :(得分:1)
您应该在阅读时检查ID。将对象添加到dbcontext时,该值必须为0。您可以对其进行强制编辑:reading.Id = 0;