Вставка ядра ASP.NET EF в несколько таблиц с внешним ключом - PullRequest
0 голосов
/ 29 декабря 2018

Я действительно не могу понять это.Я постоянно сталкиваюсь с этой ошибкой, и я не уверен, как изменить код для поддержки 1 для многих.Приведенные мною примеры довольно сложны для понимания.Некоторые предлагают изменить свободный API или модель или даже контроллер.

Ошибка:

SqlException: невозможно вставить явное значение для столбца идентификаторов в таблице 'CompetitionCategory', когда для IDENTITY_INSERT установлено значение OFF.
System.Data.SqlClient.SqlCommand + <> c.b__122_0 (результат задачи)

DbUpdateException: при обновлении записей произошла ошибка.Подробности см. Во внутреннем исключении.

Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync (соединение IRelationalConnection, CancellationToken cancellationToken)

Competition класс модели:

public class Competition
{
        [Key]
        public int ID { get; set; }
        [Required]
        [Display(Name = "Competition Name")]
        public string CompetitionName { get; set; }
        [Required]
        public string Status { get; set; }

        public ICollection<CompetitionCategory> CompetitionCategories { get; set; }
}   

CompetitionCategory класс модели:

public class CompetitionCategory
{
        [Key]
        public int ID { get; set; }
        [Required]
        [Display(Name = "Category Name")]
        public string CategoryName { get; set; }

        [ForeignKey("CompetitionID")]
        public int CompetitionID { get; set; }
}

После некоторой обработки я понял, что для передачи списка контроллеру мне следует использовать модель представления, как показано здесь:

public class CategoriesViewModelIEnumerable
{
        public Competition competition { get; set; }
        public CompetitionCategory competitionCategory { get; set; }

        // From Microsoft
        public IEnumerable<string> SelectedCategories { get; set; }

        public List<SelectListItem> CategoriesList { get; } = new List<SelectListItem>
        {
            new SelectListItem { Value = "xxx", Text = "xxx" },
            new SelectListItem { Value = "yyy", Text = "yyy" },
            new SelectListItem { Value = "zzz", Text = "zzz" },
         };
}

Я могу успешно передать данные на мой контроллер и прочитать / распечатать их на консоли.Однако я нажимаю на последнюю ошибку, которая заключается в сохранении 2-й категории и далее в базе данных, возможно, из-за некоторых ограничений первичного ключа / внешнего ключа.

В настоящее время я могу сохранить только первый элемент в списке в базе данных.

public async Task<IActionResult> Create(CategoriesViewModelIEnumerable model)
{
    if (ModelState.IsValid)
    {
        CompetitionCategory competitionCategory = new CompetitionCategory();
        _context.Add(model.competition);
        await _context.SaveChangesAsync();

        foreach (var CategoryName in model.SelectedCategories)
        {
            competitionCategory.CategoryName = CategoryName;
            competitionCategory.CompetitionID = model.competition.ID;
            _context.Add(competitionCategory);
            await _context.SaveChangesAsync();
        }

        await _context.SaveChangesAsync();
    }
}

Благодарим Вас за помощь!:)

Ответы [ 4 ]

0 голосов
/ 30 декабря 2018

Чтобы исправить ошибку, вы можете рассмотреть возможность сохранения явных идентификаторов во внешнем ключе, но EF Core устанавливает первичный ключ в таблице с включенной идентификационной вставкой.Чтобы явно отключить его, в вашем DbContext вы можете переопределить метод OnModelCreating, если вы еще этого не сделали, и поместить в эту строку:

protected override OnModelCreating(DbModelBuilder modelBuilder){
//some more code if necessary - define via Fluent api below
modelBuilder.Entity<CompetitionCategory>().Property(t => t.CompetitionID)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
//define also other ids to not have databasegeneration option set to identity to allow explicit idsif required
}

Также вы можете сначала рассмотреть возможность сохранения Competition и CompetitionCategory внутри транзакциитак что вы можете откатить или зафиксировать транзакцию, если возникла ошибка или нет, используя TransactionScope в EF.В любом случае вы получаете ошибку из-за того, что вы указали явный идентификатор, а EF по умолчанию установит столбец идентификатора с вставкой идентификатора, если явно не указано иное.Вы можете использовать атрибут databasegeneratedoption, если это более удобно.См. Атрибут DatabaseGeneratedOption

0 голосов
/ 30 декабря 2018

Редактировать: Этот ответ работает, если он не ясен

Большое спасибо Келсо Шарпу за то, что он указал мне правильное направление.Исправлено это путем редактирования контроллера.

Для дальнейшего использования другими пользователями:

В основном вам просто нужно добавить свою "основную модель", в данном случае Competition.

Поскольку Competition уже имеет коллекцию CompetitionCategory, инициализируйте ее и добавьте каждый CompetitionCategory в коллекцию, как показано в цикле for.

Наконец, добавьте модель Competition в базу данных.Я предполагаю, что EF Core автоматически добавит данные Коллекции в таблицу CompetitionCategory для вас после сопоставления внешнего ключа.(Кто-то, пожалуйста, измените это, если это не так)

Рабочий контроллер:

public async Task<IActionResult> Create(CategoriesViewModelIEnumerable model)
{
    if (ModelState.IsValid)
    {
    model.competition.CompetitionCategories = new Collection<CompetitionCategory>();
        foreach (var CategoryName in model.SelectedCategories)
        {
             model.competition.CompetitionCategories.Add(new CompetitionCategory { CompetitionID=model.competition.ID, CategoryName=CategoryName});
        }
        _context.Add(model.competition);
        await _context.SaveChangesAsync();
    }
}
0 голосов
/ 30 декабря 2018

Я полагаю, что проблема (на самом деле не проверяла) заключается в том, что вы создаете экземпляр своей сущности CompetitionCategory один раз, а затем пытаетесь добавить этот единственный экземпляр в модель для каждой итерации в цикле foreach.

Вы должны создать новый экземпляр CompetitionCategory для каждой итерации, а затем добавить каждый новый права доступа к модели:

foreach (var CategoryName in model.SelectedCategories) 
{ 
  CompetitionCategory competitionCategory = new CompetitionCategory();
  competitionCategory.CategoryName = CategoryName;
  competitionCategory.CompetitionID = model.competition.ID;
  _context.Add(competitionCategory);
}

await _context.SaveChangesAsync();
0 голосов
/ 29 декабря 2018

Что ж, я потенциально вижу 2 проблемы.

  1. Вы делаете это асинхронно, поэтому возможно, что он пытается сохранить изменения второй базы данных до того, как первые будут завершены.
  2. Вы должны создать полную модель, а затем добавить ее, второе добавление в этом случае должно быть обновлением, поскольку вы уже добавили его с первым и сохранили изменения, поэтому было бы лучше создать вашу модель полностьюсо всеми данными и добавить его, а затем сохранить изменения.

       CompetitionCategory competitionCategory = new CompetitionCategory();
       foreach (var CategoryName in model.SelectedCategories)
       {
           competitionCategory.CategoryName = CategoryName;
           competitionCategory.CompetitionID = model.competition.ID;
    
        }
       _context.Add(model.competition);
        await _context.SaveChangesAsync();
    
...