SqlException: Невозможно вставить явное значение для столбца идентификаторов в таблице [Имя таблицы], когда для IDENTITY_INSERT установлено значение OFF - PullRequest
1 голос
/ 30 июня 2019

Я создаю веб-сайт для отслеживания расходов, и когда я отправляю новый запрос POST, вызывающий метод AddNew для добавления новой записи, я получаю эту ошибку:

SqlException: невозможно вставить явное значение для столбца идентификаторов в таблице «Счета», если для параметра IDENTITY_INSERT задано значение OFF. Невозможно вставить явное значение для столбца идентификаторов в таблице «Категории», если для параметра 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

Учетная запись с идентификатором == 1 и категория с идентификатором == 1 уже существуют в соответствующих базах данных.

Кроме того, когда я вставляю значения в базу данных учетных записей, чей метод выглядит точно так же, как AddNew для записей, он работает без проблем.

Кто-нибудь знает, о чем это?

Ответы [ 2 ]

1 голос
/ 01 июля 2019

Проблема в том, что вы передаете сущности с отношениями обратно на сервер. Каждый запрос обслуживается различным контекстом БД, поэтому, хотя ваша запись с соответствующей учетной записью и категорией выглядит как сущности, это не так. Это десериализованные объекты POCO, о которых DbContext ничего не знает. Когда вы передаете сущность, ссылающуюся на другие сущности, которые уже существуют в базе данных, получающий контекст не знает об этих сущностях, поэтому он интерпретирует их как новые записи и пытается вставить их.

Моя рекомендация по умолчанию - никогда не передавать объекты между клиентом и сервером. Это приводит к ошибкам сериализации и проблемам с производительностью от сервера к клиенту, а также подвергает вашу систему подделке, а также таким проблемам, как ошибки вставки или дублирование при использовании от клиента к серверу.

Чтобы решить вашу проблему без особых изменений:

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. Это также будет работать, однако это может сделать вашу систему уязвимой для взлома. Данные, передаваемые обратно в контроллер, могут быть перехвачены средствами отладки или анализаторами на клиентском компьютере, где данные в сообщении могут быть изменены для изменения деталей в записях и связанных с ними записях способами, которые вы не собираетесь использовать. Если по какой-либо причине какой-либо код заканчивает тем, что устанавливает эти повторно присоединенные объекты в состояние Modified, эти изменения будут сохранены в базе данных на SaveChanges. Присоединение сущностей также может создавать неудобства при работе с несколькими отдельными ссылками. Например, если вы сохраняете несколько записей одновременно с родственниками. Присоединение каждого из них будет работать нормально, когда все родственники уникальны, но без дополнительной логики проверки каждого из них на предмет наличия уже прикрепленной ссылки вы можете получить ошибки при попытке второй ссылки на уже присоединенное (или загруженное) присоединение объекта , Это означает больше кода для проверки локальной ассоциации и замены, если он найден.

0 голосов
/ 04 июля 2019

Я только что изменил модель 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 в качестве внешних ключей.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...