SqlException:当IDENTITY_INSERT设置为OFF

时间:2019-06-30 14:56:06

标签: c# sql entity-framework asp.net-core-2.2

我正在创建一个用于费用跟踪的网站,当我发送一个调用AddNew方法的新POST请求以添加新条目时,出现此错误:

  

SqlException:当IDENTITY_INSERT设置为OFF时,无法为表'Accounts'中的Identity列插入显式值。当IDENTITY_INSERT设置为OFF时,无法为表“类别”中的身份列插入显式值。

public class Entry
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Description { get; set; }
    public decimal MoneyAmount { get; set; }
    public virtual Account Account { get; set; }
    public virtual Category Category { get; set; }
    public string CreatedTimestamp { get; set; }
}

public class Account
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual User User { get; set; }
    //public byte[] BankApi { get; set; }
    public decimal MoneyAmount { get; set; }
    public string CreatedTimestamp { get; set; }
    public virtual ICollection<Entry> Entries { get; set; }
}

public class Category
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public virtual EntryType EntryType { get; set; }
    public virtual ICollection<Entry> Entries { get; set; }
}

public interface IEntryService
{
    void AddNew(Entry entry);
}

public class EntryService : IEntryService
{
    private DataContext _context;

    public EntryService(DataContext context)
    {
        _context = context;
    }

    public void AddNew(Entry entry)
    {
        entry.CreatedTimestamp = DateTime.Now.ToString();

        _context.Entries.Add(entry);
        _context.SaveChanges();
    }
}

这是我的邮递员要求:

Postman request

ID == 1的帐户和ID == 1的类别都已经存在于各自的数据库中。

此外,当我向Account数据库插入值时(其输入方法与AddNew完全相同),它可以正常工作。

有人知道这是怎么回事吗?

2 个答案:

答案 0 :(得分:1)

问题是您正在将具有关联关系的实体传递回服务器。每个请求均由不同的数据库上下文服务,因此,与它关联的“帐户”和“类别”的条目看起来像实体,但它们却不是。它们是DbContext不知道的反序列化POCO对象。当您传递引用数据库中已经存在的其他实体的实体时,接收上下文不了解这些实体,因此它将它们解释为新记录并尝试插入它们。

我的默认建议是不要在客户端和服务器之间传递实体。这会导致从服务器到客户端的序列化错误和性能问题,并使您的系统遭受篡改,以及从客户端到服务器使用时出现诸如插入错误或重复等问题。

无需太多更改即可解决您的问题:

public void AddNew(Entry entry)
{
    entry.CreatedTimestamp = DateTime.Now.ToString();
    var account = _context.Accounts.Single(x => x.Id = entry.Account.Id);
    var category = _context.Categories.Single(x => x.Id = entry.Category.Id);
    entry.Account = account; 
    entry.Category = category;
    _context.Entries.Add(entry);
    _context.SaveChanges();
}

这会将条目帐户和类别与上下文已知的实例相关联。

另一种方法是将传递回DbContext的帐户和类别附加。这也可以,但是可能会使您的系统容易受到篡改。传递回控制器的数据可能会被客户端计算机上的调试工具或嗅探器截获,在其中消息中的数据可能会更改,从而以您不希望的方式更改记录及其相关记录中的详细信息。如果由于某种原因最终导致任何代码将这些重新连接的实体设置为“已修改”状态,则这些更改将保存到SaveChanges上的数据库中。当处理多个分离的引用时,附加实体也可能很麻烦。例如,如果您一次与亲戚一起保存多条记录。当所有亲戚都是唯一的时,附加每个附件可以很好地工作,但是没有额外的逻辑来检查每个亲戚以查看是否已经有附加引用,则当尝试对已附加(或加载)的实体附加附件进行第二次引用时,您可能会出错。这意味着需要更多代码来检查本地关联,并在找到本地关联后将其替换。

答案 1 :(得分:0)

我刚刚从

更改了Entry模型
public class Entry
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Description { get; set; }
    public decimal MoneyAmount { get; set; }
    public virtual Account Account { get; set; }
    public virtual Category Category { get; set; }
    public string CreatedTimestamp { get; set; }
}

对此:

 public class Entry
    {
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int Id { get; set; }
        public string Description { get; set; }
        public decimal MoneyAmount { get; set; }
        public int AccountId { get; set; }
        public int CategoryId { get; set; }
        public string CreatedTimestamp { get; set; }
    }

现在一切似乎都正常了。 C#发挥了魔力,并选择AccountId和CategoryId作为外键。