使用EF Core 2.1将记录从ASP .Net Core 2.1 Web API插入SQL Server数据库时发生错误

时间:2018-11-08 19:25:10

标签: sql-server asp.net-core-2.1 ef-core-2.1 asp.net-core-webapi-2.1

我创建了一个新的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列,因为从数据库中读取时需要它...有什么想法吗?

2 个答案:

答案 0 :(得分:1)

实体框架已经可以解决您要解决的问题。我相信您的问题出在其他地方。

EF的默认行为

新建属性为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;