Не удается обновить базу данных с помощью EF Core AddRange

#c# #entity-framework-core

Вопрос:

Я получаю следующую ошибку с кодом ниже:

  • Я объявляю свой репозиторий с помощью AddScoped в своем api restful, когда я использую это. По-прежнему не работает. (услуги.AddScoped<ICampRepository, CampRepository>();)
  • Я пробовал использовать AsNoTracking(), но SaveChanges не работает. Даже когда я говорю EF, что состояние изменилось, я снова получаю ту же ошибку.

Экземпляр сущности типа «Конференц-зал» не может быть отслежен, поскольку другой экземпляр с тем же значением ключа для {‘Id’} уже отслеживается. При присоединении существующих сущностей убедитесь, что присоединен только один экземпляр сущности с заданным значением ключа. Рассмотрите возможность использования DbContextOptionsBuilder.Позволяет чувствительному каталогизированию» видеть конфликтующие ключевые значения.

         public async Task<bool> AddRangeTalksAsync(List<Talk> talksToBulkInsert, string moniker)
        {
            try
            {
                IQueryable<Camp> query = _context.Camps.Where(c => c.Moniker == moniker);

                Camp camp = await query.FirstOrDefaultAsync();

                camp.Talks.AddRange(talksToBulkInsert);

                await _context.SaveChangesAsync();

                return true;
            }
            catch (Exception ex)
            {
                return false;
            }
        }
 

Ниже приведен пример списка объектов, которые я пытаюсь вставить. Один работает, но не больше, чем один не работает.

 [
   {
      "id": 0,
      "campId": 0,
      "title": "Talk for 'CompuGen Core Code Camp #4' for conference room 'Conference Room #1' for day 1 hour 1",
      "abstract": "This talk is interesting, you will like it.",
      "level": 1,
      "food": "Bread",
      "beverage": "Water",
      "swag": "Talk T-Shirt and a notebook",
      "startTime": "2022-01-03T08:00:00",
      "endTime": "2022-01-03T09:00:00",
      "conferenceRoom": {
         "id": 307,
         "campId": 34,
         "conferenceRoomName": "Conference Room #1",
         "conferenceRoomCapacity": 56
      },
      "talkSpeakers": [],
      "talkAttendees": [],
      "ratings": []
   },
{
      "id": 0,
      "campId": 0,
      "title": "Talk for 'CompuGen Core Code Camp #4' for conference room 'Conference Room #1' for day 1 hour 2",
      "abstract": "This talk is interesting, you will like it.",
      "level": 1,
      "food": "Bread",
      "beverage": "Water",
      "swag": "Talk T-Shirt and a notebook",
      "startTime": "2022-01-03T08:00:00",
      "endTime": "2022-01-03T09:00:00",
      "conferenceRoom": {
         "id": 307,
         "campId": 34,
         "conferenceRoomName": "Conference Room #1",
         "conferenceRoomCapacity": 56
      },
      "talkSpeakers": [],
      "talkAttendees": [],
      "ratings": []
   }
]
 

Кроме того, схема базы данных выглядит так из сущностей, которые я создал в EF CORE.

Схема базы данных для основной системы планирования конференций Camp Code

То, что я ищу, — это то, как сохранить эту схему без изменений и иметь возможность создавать несколько конференц-залов для лагеря за один раз, но это не работает.

Комментарии:

1. То, как вы это сделали, слишком сложно, могло бы быть проще. Вы пытаетесь создать новый конференц-зал, когда создаете talk, или просто добавляете конференц-зал 307, который уже существует?

2. Просто пытаюсь создать коллекцию конференц-залов, добавленных в лагерь.

Ответ №1:

Попробуйте это

 var camp = await _context.Camps.Where(c => c.Moniker == moniker).FirstOrDefaultAsync();

if (camp!=null)
{
var campId=camp.Id;
 talksToBulkInsert.ForEach(i=> i.CampId=campId);

 _context.Talks.AddRange(talksToBulkInsert);
 await _context.SaveChangesAsync()
}
 

вы тоже можете попробовать это, но это не так надежно

 var camp = await _context.Camps.Include(i=> i.Talks)
.Where(c => c.Moniker == moniker).FirstOrDefaultAsync();

 camp.Talks.AddRange(talksToBulkInsert);
 

но сначала вам нужно исправить структуру таблиц данных, например

 "conferenceRoom": {
         "id": 307,
         "campId": 34,
.....
 

Как получилось, что у дочерней записи есть campId, а у родительской-нет? у вас вообще не должно быть здесь никакого кампида. И их гораздо больше. Сначала вам нужно создать действительную базу данных и только после этого попытаться что-то добавить или обновить.

Комментарии:

1. Я попробовал тот первый метод, который вы предложили, и все равно получил ту же проблему.

2. Я также попробовал второй метод, и все равно получаю ту же проблему.

3. @BillBlair Я тоже не ожидал ничего хорошего, так как ваша структура данных полна ошибок. Он никогда не будет работать должным образом.

4. Хорошо, так что же в этом плохого? Конференц — зал-это то, из-за чего он выходит из строя, как я должен изменить структуру данных?

5. Json действителен, поэтому я не уверен, что с ним не так.

Ответ №2:

Хорошо, я думаю, что на данный момент я это исправил. Что я сделал, так это изменил способ создания стола переговоров. Для внешнего ключа конференц-зала у меня есть следующее для OnDelete: OnDelete: ReferentialAction.Никаких действий. Таким образом, это удалит циклическую ссылку между ConferenceRoomId и ConferenceRoom. Однако, если конференц-зал будет удален, соответствующий разговор удален не будет. Это может быть нежелательно, но я могу удалить эти данные другим способом, если это необходимо.

Мое новое Определение для выступления:

 public class Talk
    {
        /// <summary>
        /// Constructor for Talk.
        /// </summary>
        public Talk()
        {
            Ratings = new List<Rating>();
            TalkSpeakers = new List<TalkSpeaker>();
            TalkAttendees = new List<TalkAttendee>();
        }

#pragma warning disable 1591 //Ingore compiler warning for not having XML comments.

        [ExcludeFromCodeCoverage]
        public int Id { get; set; }

        [ExcludeFromCodeCoverage]
        public int CampId { get; set; }

        /// <summary>
        /// Navigation property to a Camp.
        /// </summary>
        public Camp Camp { get; set; }

        /// <summary>
        /// Navigation property to ratings.
        /// </summary>
        public List<Rating> Ratings { get; set; }

        public int ConferenceRoomId { get; set; }

        /// <summary>
        /// Navigation property to a Conference Room.
        /// </summary>
        public ConferenceRoom ConferenceRoom { get; set; }

        public string Title { get; set; }
        public string Abstract { get; set; }
        public int? Level { get; set; }
        public string Food { get; set; }
        public string Beverage { get; set; }
        public string SWAG { get; set; }
        public DateTime startTime { get; set; } = DateTime.MinValue;
        public DateTime endTime { get; set; } = DateTime.MinValue;

        /// <summary>
        /// TalkSpeaker joins Talk and Speaker together.
        /// </summary>
        public List<TalkSpeaker> TalkSpeakers { get; set; }

        /// <summary>
        /// TalkAttendee joins Talk and Attendee together.
        /// </summary>
        public List<TalkAttendee> TalkAttendees { get; set; }

#pragma warning restore 1591

    }
 

Изменение внешнего ключа в конференц-залах, чтобы не было круговой ссылки между идентификатором конференц-зала и конференц-залом.

             migrationBuilder.CreateTable(
                name: "Talks",
                columns: table => new
                {
                    Id = table.Column<int>(nullable: false)
                        .Annotation("SqlServer:Identity", "1, 1"),
                    CampId = table.Column<int>(nullable: false),
                    ConferenceRoomId = table.Column<int>(nullable: false),
                    Title = table.Column<string>(nullable: true),
                    Abstract = table.Column<string>(nullable: true),
                    Level = table.Column<int>(nullable: true),
                    Food = table.Column<string>(nullable: true),
                    Beverage = table.Column<string>(nullable: true),
                    SWAG = table.Column<string>(nullable: true),
                    startTime = table.Column<DateTime>(nullable: false),
                    endTime = table.Column<DateTime>(nullable: false)
                },
                constraints: table =>
                {
                    table.PrimaryKey("PK_Talks", x => x.Id);
                    table.ForeignKey(
                        name: "FK_Talks_Camps_CampId",
                        column: x => x.CampId,
                        principalTable: "Camps",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.Cascade);
                    table.ForeignKey(
                        name: "FK_Talks_ConferenceRooms_ConferenceRoomId",
                        column: x => x.ConferenceRoomId,
                        principalTable: "ConferenceRooms",
                        principalColumn: "Id",
                        onDelete: ReferentialAction.NoAction);
                });