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 в результирующем запросе.
Убрал приведенный выше код, перестроил и все работает как надо.