InsertAndGetIdAsync - невозможно вставить явное значение для столбца идентификаторов

Использование ASPNET Boilerplate (.NET Framework) 3.6.1

Я успешно использовал AsyncCrudAppService на нескольких сущностях без проблем, но я столкнулся с проблемой с одним конкретным типом сущностей, который я не могу вставить, поскольку Entity Framework, кажется, думает, что ему предоставлен явный идентификатор.

Моя сущность:

public class Attendance : FullAuditedEntity<int>
{
    [Required]
    public Guid Guid { get; set; }

    public long? UserId { get; set; }
    [Required]
    public int EventId { get; set; }
    public int StatusId { get; set; }

    [Required]
    [MaxLength(100)]
    public string InviteEmail { get; set; }
    [Required]
    [MaxLength(20)]
    public string InviteCode { get; set; }

    public bool InviteAccepted { get; set; }

    public DateTime? InviteAcceptedDate { get; set; }

    public string InviteSource { get; set; }
    public long? InvitedByUserId { get; set; }

    [ForeignKey("UserId")]
    public User User { get; set; }
    [ForeignKey("InvitedByUserId")]
    public User InvitedByUser { get; set; }

    [ForeignKey("EventId")]
    public Event Event { get; set; }

    [ForeignKey("StatusId")]
    public AttendanceStatus Status { get; set; }
}

Мой CreateDto

[AutoMapTo(typeof(Attendance))]
public class CreateAttendanceDto
{
    public long? UserId { get; set; }
    [Required]
    public int EventId { get; set; }
    public int StatusId { get; set; }

    [Required]
    [MaxLength(100)]
    public string InviteEmail { get; set; }

    public bool InviteAccepted { get; set; }

    public DateTime? InviteAcceptedDate { get; set; }
    public string InviteSource { get; set; }
    public long? InvitedByUserId { get; set; }

}

Мой AppService Создать метод:

public override async Task<AttendanceDto> Create(CreateAttendanceDto input)
    {
        var attendance = ObjectMapper.Map<Attendance>(input);

        attendance.InviteCode = "TEST";

        // Create GUID
        attendance.Guid = Guid.NewGuid();

        await _attRepository.InsertAndGetIdAsync(attendance);
        return MapToEntityDto(attendance);
    }

Отладка объекта показывает, что отправляет идентификатор:0 в метод вставки. Это точно так же, как другой объект вставки, который работает нормально.

Отслеживая два разных запроса на вставку сущностей в SQL Profiler, я вижу, что рабочий удалил идентификатор из запроса на вставку, где тот, который выше, не работает, пытается вставить id=0 в базу данных, что явно проблема.

Ошибка, возвращаемая вызовом InsertAndGetIdAsync, выглядит следующим образом:

ОШИБКА 2018-07-10 17:29:06,249 [108 ] nHandling.AbpApiExceptionFilterAttribute - Произошла ошибка при обновлении записей. Смотрите внутреннее исключение для деталей. System.Data.Entity.Infrastructure.DbUpdateException: при обновлении записей произошла ошибка. Смотрите внутреннее исключение для деталей. ---> System.Data.Entity.Core.UpdateException: при обновлении записей произошла ошибка. Смотрите внутреннее исключение для деталей. ---> System.Data.SqlClient.SqlException: Невозможно вставить явное значение для столбца идентификаторов в таблице 'Attendance', когда для IDENTITY_INSERT установлено значение OFF.

Я проверил и дважды проверил различия между этими двумя Creates appservice и их связанными сущностями и dtos, и я не могу найти никаких различий. Даже получил коллегу, чтобы проверить.

Есть идеи, что здесь происходит?

2 ответа

Вам необходимо указать сопоставление для сущности Attendance чтобы он не пытался вставить значение в столбец идентификатора. Проще говоря, вам нужно, чтобы EF знал, что столбец ID настроен для IDENTITY. Сообщение об исключении говорит об этом очень четко.

System.Data.SqlClient.SqlException: Невозможно вставить явное значение для столбца идентификаторов в таблице "Посещаемость", когда для IDENTITY_INSERT установлено значение OFF.


Используя атрибуты:

public class Attendance {
  [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
  public long AttendanceId { get; set; }
}

или в беглом отображении в DbContext тип

protected override void OnModelCreating(DbModelBuilder modelBuilder) {
  modelBuilder.Entity<Attendance>().Property(t => t.AttendanceId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
}

Я нашел проблему, которая была немного скрыта.

Внутри моего DbContext я ранее пытался присвоить полю Guid значение по умолчанию (которое, как я теперь понимаю, также делало его столбцом идентификаторов в глазах EF). В конце концов, это не сработало, но я не стал убирать это:

        modelBuilder.Entity<Attendance>()
            .Property(x => x.Guid)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

В базе данных не было никакого столбца идентификаторов в столбце Guid и ошибки, связанной со столбцом Id, но, очевидно, это сбивало с толку обработку EF значения id в результирующем запросе.

Убрал приведенный выше код, перестроил и все работает как надо.

Другие вопросы по тегам