EF Core Настройка сериализации JSON с помощью LINQ

Я создаю демонстрацию API в .NET Core и создаю вложенный объект JSON, который необходимо создать с помощью серии запросов LINQ.

Моя текущая проблема заключается в том, что когда я получаю около 4 слоев в глубину, я хочу настроить то, что на самом деле сериализуется, а точнее, я хочу «Не включать» определенные свойства навигации специально для этого запроса, но не для общих целей, только для этого конкретного запрос.

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

Я уже добавил в свой файл Startup.cs следующее, чтобы избежать циклов:

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

Сейчас у меня в действии 5 моделей, которые выглядят так:

 public class Hotel
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string StreetAddress { get; set; }
        public string City { get; set; }
        public string State { get; set; }
        public string Phone { get; set; }
        public List<HotelRooms> HotelRooms { get; set; }
    }

    public class Room
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public Layout Layout { get; set; }

        public List<RoomAmenities> RoomAmenities { get; set; }
        public List<HotelRooms> HotelRooms { get; set; }
    }

    public class HotelRooms
    {
        public int HotelID { get; set; }
        public int RoomNumber { get; set; }
        public int RoomID { get; set; }
        public decimal Rate { get; set; }
        public bool PetFriendly { get; set; }
        public Hotel Hotel { get; set; }
        public Room Room { get; set; }
    }

    public class Amenities
    {
        public int ID { get; set; }
        public string Name { get; set; }

        // Navigation Properties
        public List<RoomAmenities> RoomAmenities { get; set; }
    }

    public class RoomAmenities
    {
        public int RoomID { get; set; }
        public int AmenitiesID { get; set; }

        public Room Room { get; set; }
        public Amenities Amenity { get; set; }
    }

В моих сервисах у меня есть такая логика, которая на самом деле выполняет запросы:

        public async Task<Hotel> GetById(int id)
        {
            var hotel = await _context.Hotel.FindAsync(id);
            // Get a list of rooms
            var rooms = await _context.HotelRooms.Where(r => r.HotelID == id)
                                            .Include(d => d.Room)
                                            .ThenInclude(a => a.RoomAmenities)
                                            .ThenInclude(x => x.Amenity).ToListAsync();
            hotel.HotelRooms = rooms;

            return hotel;
        }

Текущий выход:

{
    "id": 1,
    "name": "Amanda's Hotel",
    "streetAddress": "123 CandyCane Lane",
    "city": "Seattle",
    "state": "WA",
    "phone": "123-456-8798",
    "hotelRooms": [
        {
            "hotelID": 1,
            "roomNumber": 101,
            "roomID": 2,
            "rate": 75.00,
            "petFriendly": false,
            "room": {
                "id": 2,
                "name": "Queen Suite",
                "layout": 2,
                "roomAmenities": [
                    {
                        "roomID": 2,
                        "amenitiesID": 1,
                        "amenity": {
                            "id": 1,
                            "name": "Coffee Maker",
                            "roomAmenities": [
                                {
                                    "roomID": 1,
                                    "amenitiesID": 1,
                                    "room": {
                                        "id": 1,
                                        "name": "Princess Suite",
                                        "layout": 1,
                                        "roomAmenities": [
                                            {
                                                "roomID": 1,
                                                "amenitiesID": 2,
                                                "amenity": {
                                                    "id": 2,
                                                    "name": "Mini Bar",
                                                    "roomAmenities": []
                                                }
                                            }
                                        ],
                                        "hotelRooms": [
                                            {
                                                "hotelID": 1,
                                                "roomNumber": 123,
                                                "roomID": 1,
                                                "rate": 120.00,
                                                "petFriendly": true
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    }
                ],
                "hotelRooms": []
            }
        },
        {
            "hotelID": 1,
            "roomNumber": 123,
            "roomID": 1,
            "rate": 120.00,
            "petFriendly": true,
            "room": {
                "id": 1,
                "name": "Princess Suite",
                "layout": 1,
                "roomAmenities": [
                    {
                        "roomID": 1,
                        "amenitiesID": 1,
                        "amenity": {
                            "id": 1,
                            "name": "Coffee Maker",
                            "roomAmenities": [
                                {
                                    "roomID": 2,
                                    "amenitiesID": 1,
                                    "room": {
                                        "id": 2,
                                        "name": "Queen Suite",
                                        "layout": 2,
                                        "roomAmenities": [],
                                        "hotelRooms": [
                                            {
                                                "hotelID": 1,
                                                "roomNumber": 101,
                                                "roomID": 2,
                                                "rate": 75.00,
                                                "petFriendly": false
                                            }
                                        ]
                                    }
                                }
                            ]
                        }
                    },
                    {
                        "roomID": 1,
                        "amenitiesID": 2,
                        "amenity": {
                            "id": 2,
                            "name": "Mini Bar",
                            "roomAmenities": []
                        }
                    }
                ],
                "hotelRooms": []
            }
        }
    ]
}

Это слишком вложено для меня, я бы хотел, чтобы внутренние "roomA Удобства" внутри объекта "amenity" не включались. Я бы хотел, чтобы мой объект выглядел так:

{
  "id": 1,
  "name": "Amanda's Hotel",
  "streetAddress": "123 CandyCane Lane",
  "city": "Seattle",
  "state": "WA",
  "phone": "123-456-8798",
  "hotelRooms": [
    {
      "hotelID": 1,
      "roomNumber": 101,
      "roomID": 2,
      "rate": 75,
      "petFriendly": false,
      "room": {
        "id": 2,
        "name": "Queen Suite",
        "layout": 2,
        "roomAmenities": [
          {
            "roomID": 2,
            "amenitiesID": 1,
            "amenity": {
              "id": 1,
              "name": "Coffee Maker"
            }
          }
        ],
        "hotelRooms": []
      }
    },
    {
      "hotelID": 1,
      "roomNumber": 123,
      "roomID": 1,
      "rate": 120,
      "petFriendly": true,
      "room": {
        "id": 1,
        "name": "Princess Suite",
        "layout": 1,
        "roomAmenities": [
          {
            "roomID": 1,
            "amenitiesID": 1,
            "amenity": {
              "id": 1,
              "name": "Coffee Maker"
            }
          },
          {
            "roomID": 1,
            "amenitiesID": 2,
            "amenity": {
              "id": 2,
              "name": "Mini Bar"
            }
          }
        ],
        "hotelRooms": []
      }
    }
  ]
}

Есть ли у кого-нибудь рекомендации о том, как я могу достичь этого с помощью EFCore и LINQ?


person buttercup_byte    schedule 19.02.2020    source источник
comment
›Моя первая мысль была сделать DTO. Это правильный подход. Фактически, я бы почти зашел так далеко, чтобы сказать, что вам действительно не следует возвращать модели EF из своего API, если вы можете ему помочь (в противном случае каждый раз, когда вы меняете свою модель данных, ваш контракт API изменяется, когда это может не понадобиться).   -  person MindingData    schedule 19.02.2020
comment
Ах, спасибо! Вы определенно правы, DTO - лучший подход.   -  person buttercup_byte    schedule 20.02.2020
comment
Вы пробовали не включать amenity как var rooms = await _context.HotelRooms.Where(r => r.HotelID == id).Include(d => d.Room).ThenInclude(a => a.RoomAmenities).ToListAsync();?   -  person Selim Yildiz    schedule 20.02.2020


Ответы (1)


вы можете проверить это сообщение В чем разница между PreserveReferencesHandling и ReferenceLoopHandling в Json.Net?

Я вижу, что вы уже реализовали referenceloopHandling, но даже когда вы его используете, вы можете сделать только внутреннюю ссылку нулевой, но не полностью удалить пару ключ-значение из json.

person kannangokul    schedule 19.02.2020