CancellationToken в функции контроллера .Net Core внезапно становится истинным

Я только что столкнулся с очень странной проблемой, связанной с cancelToken. Веб-API разработан с использованием .Net Core, а интерфейс — на Angular. 1 час назад я пытаюсь разработать дополнительную функцию и перед запуском запускаю ее еще раз, и вдруг отметка Token становится истинной. Такого никогда не происходит раньше. Я не вносил никаких изменений в бэкэнд и фронтенд.

Функция контроллера:

[HttpDelete("dqc")]
[AllowAnonymous]
public async Task<IActionResult> DeleteDQCAsync([FromBody] IEnumerable<DqcDto> dqcs, CancellationToken cancellationToken)
{
    try
    {
        var response = await _managerService.DeleteDqcAsync(dqcs, cancellationToken);
        if (response)
        {
            return Ok(new { message = "DQC codes have been deleted successfully" });
        }
        else
        {
            return BadRequest(new { message = "Delete selected DQC codes failed." });
        }
    }
    catch (Exception)
    {
        throw;
    }
}

Даже странно, что когда я добавляю точку останова и отлаживаю код, функция вызывается, а отмена оказывается ложной. Через секунду отметкаToken становится истинной, но я ничего не нажимаю. Иногда, когда я нажимаю кнопку во внешнем интерфейсе, функция даже не вызывается. Только когда я добавляю точку останова в браузере и выполняю построчно, токен отмены всегда имеет значение false.

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

Может ли кто-нибудь дать мне подсказку, как я могу понять, почему это происходит?

Обновлять: После нескольких часов исследования я обнаружил, что код Angular ведет себя странно. Например:

deleteItems(): void {
    let isDelete: boolean = false;
    const itemsToDelete = this.checkitems.map((item) => ({
      uicp: item.uicp,
      dqcCode: item.dqcCode,
      dqcDescription: item.dqcDescription,
      task: Object.keys(item.tasks)
        .filter((task) => item.tasks[task])
        .join(','),
      remark: item.remark,
      timestamp: item.lastUpdate,
    }));

    console.info(itemsToDelete);

    // Assuming there's a method in DataService to handle deletion
    this.dataService.deleteData(itemsToDelete).subscribe((response) => {
      console.info('Items deleted:', response);
      if (response.status === 200) {
        // Remove the deleted items from tableData
        this.tableData = this.tableData.filter(
          (item) => !this.checkitems.includes(item)
        );
        this.checkitems = [];
        isDelete = true;
      }
    });
    if (isDelete) {
      location.reload();
    }
  }

  saveItems(): void {
    let isAdd: boolean = false;
    let isUpdate: boolean = false;
    const newItems = this.checkitems
      .filter((item) => item.isNew)
      .map((item) => ({
        uicp: item.uicp,
        dqcCode: item.dqcCode,
        dqcDescription: item.dqcDescription,
        task: Object.keys(item.tasks)
          .filter((task) => item.tasks[task])
          .join(','),
        remark: item.remark,
      }));
    console.info('finish new items');

    const updatedItems = this.checkitems
      .filter((item) => !item.isNew)
      .map((item) => ({
        uicp: item.uicp,
        dqcCode: item.dqcCode,
        dqcDescription: item.dqcDescription,
        task: Object.keys(item.tasks)
          .filter((task) => item.tasks[task])
          .join(','),
        remark: item.remark,
        timestamp: item.lastUpdate,
      }));
    console.info('finish update item');

    console.info('New Items:', newItems);
    console.info('Updated Items:', updatedItems);
    if (newItems.length > 0) {
      console.info('start to add');
      this.dataService.addData(newItems).subscribe(
        (response) => {
          console.info('wait for response');
          if (response.status === 200) {
            isAdd = true;
            console.info('New items added:', response.body);
          } else {
            console.error('Unexpected response:', response);
          }
          this.checkitems.forEach((item) => {
            item.isNew = false;
            item.selected = false;
          });
          this.checkitems = [];
          //this.updateSavedItems();
        },
        (error: HttpErrorResponse) => {
          console.error('Error adding new items:', error.message);
        }
      );
    }

    if (updatedItems.length > 0) {
      console.info('start to update');
      this.dataService.updateData(updatedItems).subscribe(
        (response) => {
          console.info('wait for response');
          if (response.status === 200) {
            isUpdate = true;
            console.info('Items updated:', response.body);
          } else {
            console.error('Unexpected response:', response);
          }
          this.checkitems.forEach((item) => {
            item.isNew = false;
            item.selected = false;
          });
          this.checkitems = [];
          //this.updateSavedItems();
        },
        (error: HttpErrorResponse) => {
          console.error('Error updating items:', error.message);
        }
      );
    }
    if (isAdd || isUpdate) {
      location.reload();
    }
  }

Функции сервиса:

deleteData(items: any[]): Observable<any> {
    return this.http.delete(`${this.apiUrl}/dqc`, {'body':items});
  }

  addData(newItems: any[]): Observable<any> {
    return this.http.post<any>(`${this.apiUrl}/dqc`, newItems);
  }

   updateData(updatedItems: any[]): Observable<any> {
    return this.http.put<any>(`${this.apiUrl}/dqc`, updatedItems);
  } 

Существует две основные функции: одна — удаление элементов, а другая — обновление и/или добавление элементов. В этих двух функциях программа каким-то образом пропускает среднюю часть и сразу переходит к концу, чтобы обновить страницу, даже пропускает части, которые отправляют запросы на бэкэнд. Это приводит к тому, что для параметра cancelToken устанавливается значение true. Иногда эта проблема исчезает и все идет хорошо. Он действует так, что location.reload() является магнитом и заставляет программу выполнить эту строку первой.

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

Может быть, тайм-аут запроса отправлен FE из-за сеанса отладки?

decius 19.06.2024 22:16

@decius Не могли бы вы рассказать мне, как проверить?

AoLiGei 19.06.2024 22:25

Вы можете открыть инструменты разработки в своем браузере, нажав F12. Там вы можете увидеть отмену. Установите точку останова. И подождите 120 секунд после достижения точки останова. Я думаю, это должно стать причиной отмены.

decius 19.06.2024 22:34

@decius Я нашел, как это происходит, но понятия не имею, почему. В конце функции удаления в Angular я обновляю страницу, используя location.reload() Но почему-то программа пропускает функцию отправки запроса, а непосредственно обновляет страницу. В этом случае отправляется запрос на отмену. Я не знаю, почему это происходит

AoLiGei 19.06.2024 23:25

Можете ли вы добавить этот код в свой вопрос? Если вы не ожидаете первого вызова вашего API, это может произойти, как я мог себе представить.

decius 19.06.2024 23:40

@decius Я добавил код в Обновление.

AoLiGei 19.06.2024 23:57

Я опубликовал ответ. Причина, по которой это происходит, мне ясна. проверьте это.

decius 20.06.2024 07:40
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
0
7
73
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Проблема в том, что location.reload() выполняется непосредственно после subscibe(). Он работает параллельно — таким образом вызов вашего API не ожидается. Вот и причина отмены. Вам нужно поместить оператор location.reload() в subscribe() следующим образом:

this.dataService.deleteData(itemsToDelete).subscribe((response) => {
      console.info('Items deleted:', response);
      if (response.status === 200) {
        // Remove the deleted items from tableData
        this.tableData = this.tableData.filter(
          (item) => !this.checkitems.includes(item)
        );
        this.checkitems = [];
        location.reload();
      }
    });
  }

И кстати: вместо использования location.reload() вам следует просто вызвать действие GET этого API после завершения вызова удаления.

Это не причина. isAdd и isUpdate являются ложными. Им присваивается значение true только после получения ответа. Вы правы насчет location.reload, который находится снаружи, но он не выполняется, поскольку оба условия ложны.

D A 20.06.2024 08:28

Я не говорил об обновлении или сохранении элементов. Первоначальный вопрос был о методе удаления. И тут явно причина, что перезагрузка происходит вне подписки. Флаги isAdd или isUpdate отсутствуют. Итак, я ответил на первоначальный вопрос перед редактированием поста. Я не вижу причин голосовать против.

decius 20.06.2024 08:34

В других методах могут быть другие проблемы. Затем напишите второй ответ и поговорите об этом, вместо того, чтобы голосовать против моего ответа.

decius 20.06.2024 08:36

Это та же идея для удаления. isDelete имеет значение true только после ответа и всегда имеет значение false, когда location.reload(); достигается.

D A 20.06.2024 08:37

Что тогда является причиной отмены? Это также не ваши логические флаги. Код был неправильным в части перезагрузки. И этот ответ правильный. Я не услышал от тебя ответа.

decius 20.06.2024 08:41

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

D A 20.06.2024 08:49

Позиция всех «если» также неверна. Именно это я и отметил. Благодаря моему решению эти флаги вам больше не нужны. Возможно, это не решит всей проблемы, но является первым шагом в правильном направлении. Ваше поведение неконструктивно с моей точки зрения.

decius 20.06.2024 08:52

@decius Ваше решение помогает решить проблему, чтобы избежать установки для параметра cancelToken значения true, поэтому я отмечаю его как полезное. Но я действительно не понимаю, почему location.relocate() выполняется раньше другого кода, даже если он находится в последней строке. Даже если код ответа не выполняется. Также я обнаружил, что мои функции отправляют запросы на серверную часть. Я должен использовать Observable<HttpResponse<string>> и добавить указание типа ответа, иначе возвращаемый ответ не будет иметь свойства Status. Я не знаю почему, но это также помогает избежать ошибок.

AoLiGei 20.06.2024 10:17

Это трудно сказать по этим строкам кода. Но, как я уже говорил, удалите все логические флаги и поместите перезагрузку в функцию subscribe(), а затем проверьте еще раз. Или еще лучше: вместо reload() выполните вызов GET к вашему API, чтобы получить новые данные. Если бы вы использовали, например, RxJS, было бы еще проще работать с этими Obserables, потому что тогда вы можете связывать различные вызовы один за другим.

decius 20.06.2024 10:28

@decius Спасибо за советы. Я новичок в Angular, поэтому развиваюсь во время обучения. Я это отмечу. Еще раз спасибо.

AoLiGei 20.06.2024 10:37

Пожалуйста. Конечно, вы учитесь. это нормально. удаление bools и установка перезагрузки в нужное место решили вашу проблему?

decius 20.06.2024 10:46

@decius да, так и было. Но я просто не знаю, почему эта строка выполняется заранее.

AoLiGei 20.06.2024 10:51

Из-за операторов if его фактически не следует выполнять. Но использовать этот оператор if также является плохой практикой, поскольку из-за некоторых проблем с параллелизмом во время отладки это может привести к выполнению в другом порядке. Основной вывод заключается в том, что ваш вызов удаления и вызов reload() (без if) выполняются параллельно, то есть одновременно, поскольку subscribe() не будет ждать, пока операция внутри (ваш вызов удаления) не завершится. .

decius 20.06.2024 10:54

Если теперь все стало яснее и мой ответ помог решить проблему, не стесняйтесь принять и ответ. Спасибо. :)

decius 20.06.2024 10:57

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

Быстрая параллельная загрузка множества больших двоичных объектов в Azure в .NET
Является ли суффикс контроллера обязательным в .NET Core (.NET 6)?
EntraID: как передать роли приложения ниже по течению
Asp.net core 8 выбирает несколько с выбранным по умолчанию, не работает после обновления
Параллельные задачи. Запускайте параллельные потоки, но не ждите завершения других задач и получайте последние данные из базы данных
Ошибка «МАНИФЕСТ НЕИЗВЕСТНО» при публикации стандартного веб-приложения ASP.NET Core в приложениях-контейнерах Azure
.NET Core, недопустимое состояние ModelState в навигационных свойствах AllowNull в простой модели продуктов и категорий
Как установить тайм-аут для поиска операции с Mongo в С#
Я использую VS Code, как мне плавно перейти с ASP.NET Core из .NET 7 на .NET 8?
Как настроить решение C#, такое как внутренние зависимости и зависимости NuGet, на любом ПК без дополнительных ручных настроек, зависящих от ПК?