#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 при десериализации.