Как избежать циклической ссылки asp.net ядро mvc [HttpPost(«add-recipe»)] веб-api

#c# #asp.net-core-mvc #asp.net-core-webapi

#c# #asp.net-core-mvc #asp.net-core-webapi

Вопрос:

Мой проект — это приложение с рецептами (приготовлением) .NET Core 5.0

И у меня проблема с добавлением нового веб-api рецепта (HttpPost)

На postman мой ответ:

«Был обнаружен возможный объектный цикл. Это может быть связано либо с циклом, либо с тем, что глубина объекта превышает максимально допустимую глубину 32. Рассмотрите возможность использования ReferenceHandler .Сохранить в JsonSerializerOptions для поддержки циклов «.

Когда я создаю новый рецепт, он должен использовать recipeToCreateDto вместо Recipe — который содержит все свойства (циклическая ссылка)

Не могли бы вы помочь мне, как заставить его работать должным образом. Как сопоставить и т. Д.

https://i.postimg.cc/Mphv7zRH/screen.png <- скрин здесь

Я использую AutoMapper для сопоставления классов и шаблона репозитория.

 public class AppUser
    {
        public int Id { get; set; }
        public string UserName { get; set; }
        public ICollection<Recipe> Recipes {get; set;} 
    }
}
  

У пользователя много рецептов.

  public class Recipe
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public AppUser AppUser { get; set; }
        public int AppUserId { get; set; }
    }
  

Объект передачи данных

 public class RecipeForCreateDto
    {
        [Required]
        [StringLength(50, MinimumLength = 3, ErrorMessage = "You must specify name between 3 and 50 characters")]
        public string Name { get; set; }
        public int AppUserId { get; set; }
        public int AuthorId { get; set; }
    }
  

В моем AutoMapperProfiles.cs

 public class AutoMapperProfiles : Profile
    {
        public AutoMapperProfiles()
        {
        CreateMap<RecipeForCreateDto, Recipe>();
        }
  

Интерфейс рецепта

   public interface IRecipeRepository
    {
      Task<Recipe> AddNewRecipe(Recipe recipe);
    }

    public class RecipeRepository : IRecipeRepository
    {
        private readonly DataContext _context;
        private readonly IMapper _autoMapper;
        public RecipeRepository(DataContext context, IMapper autoMapper)
        {
            _autoMapper = autoMapper;
            _context = context;
        }

 public async Task<Recipe> AddNewRecipe(Recipe recipe)
        {
            await _context.Recipes.AddAsync(recipe);
            await _context.SaveChangesAsync();

            return recipe;
        }
  

}

Пользовательский контроллер:

User.GetUserName() — это статический метод, который получает имя пользователя пользователя.

 [HttpPost("add-recipe")]
        public async Task<ActionResult> AddNewRecipe(RecipeForCreateDto recipeForCreateDto)
        {
            var userFromRepo = await _userRepository.GetUserByUsernameAsync(User.GetUsername());

            recipeForCreateDto.Name = recipeForCreateDto.Name.ToLower();

            if (await _recipeRepository.RecipeExists(recipeForCreateDto.Name))
                return BadRequest("Recipe with that name already exists!");

            var recipeToCreate = _autoMapper.Map<Recipe>(recipeForCreateDto);

            recipeToCreate.AppUserId = userFromRepo.Id;


            var createdRecipe = await _recipeRepository.AddNewRecipe(recipeToCreate); // here is problem 

            var recipeToReturn = _autoMapper.Map<RecipeForDetailDto>(createdRecipe);


            return CreatedAtRoute("GetRecipe", new { controller = "Recipes", id = createdRecipe.Id }, recipeToReturn);
        }
  

Ответ №1:

«Был обнаружен возможный объектный цикл. Это может быть связано либо с циклом, либо с тем, что глубина объекта превышает максимально допустимую глубину 32. Рассмотрите возможность использования ReferenceHandler .Сохранить в JsonSerializerOptions для поддержки циклов «.

Для решения этой проблемы вы можете добавить следующий код в метод startup.cs ConfigureServices :

 services.AddControllersWithViews()
    .AddNewtonsoftJson(options =>
    options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
  

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

1. Спасибо за ответ, но у меня проблема с отображением через automapper..

Ответ №2:

 [HttpPost("{recipeForCreateDto}")]
public async Task < ActionResult > AddNewRecipe([FromBody] RecipeForCreateDto recipeForCreateDto) {
    var userFromRepo = await _userRepository.GetUserByUsernameAsync(User.GetUsername());

    recipeForCreateDto.Name = recipeForCreateDto.Name.ToLower();

    if (await _recipeRepository.RecipeExists(recipeForCreateDto.Name)) return BadRequest("Recipe with that name already exists!");

    var recipeToCreate = _autoMapper.Map < Recipe > (recipeForCreateDto);

    recipeToCreate.AppUserId = userFromRepo.Id;

    var createdRecipe = await _recipeRepository.AddNewRecipe(recipeToCreate); // here is problem 
    var recipeToReturn = _autoMapper.Map < RecipeForDetailDto > (createdRecipe);

    return CreatedAtRoute("GetRecipe", new {
            controller = "Recipes",
            id = createdRecipe.Id
        },
        recipeToReturn);
}
  

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

1. [HttpPost(«{recipeForCreateDto} «)] ? Я не понимаю, что ты пытаешься сделать

Ответ №3:

Важно отметить, что сериализатор по умолчанию в .Net теперь из System.Text.Json.Пространство имен сериализации. Этот сериализатор на самом деле отлично справляется как с сериализацией, так и с десериализацией циклических ссылок.

Чтобы включить эту функцию, поместите следующий код в свой Startup.cs:

 services
   .AddControllers()
   .AddJsonOptions(c => c.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.Preserve);
  

Документировано здесь: https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-preserve-references

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

1. но это работает иначе, чем ‘Newtonsoft.Json. Обработка ссылок. Игнорируйте ‘, поскольку он добавляет эти свойства ‘$ id’ или ‘$ ref’. Есть ли способ получить тот же результат, что и Newtonsoft?

2. Я не знаком с тем, что Newtonsoft использует для ссылок. Эти ссылки необходимы для восстановления отношений в js или .net при десериализации.