Свойства навигации по коллекции, возвращающие нечетную иерархию JSON - PullRequest
0 голосов
/ 04 октября 2018

У меня есть 2 класса:

    public class A
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public B myB { get; set; }
    }

    public class B
    {
        public int Id { get; set; }
        public string Name { get; set; }
        public ICollection<A> myAs { get; set; }
    }

Я использую Postman для проверки вызовов Api.

    public IEnumerable<B> GetBs()
    {
       return _context.Bs.Include(b => b.myAs).ToList();
    }

возвращает ожидаемый результат, объекты B и список связанных с ними объектов A:

{
        "Id": 1,
        "Name": "B2",
        "myAs": [
            {
                "Id": 1,
                "Name": "A1"
            },
            {
                "Id": 2,
                "Name": "A2"
            }
        ]
    },
    {
        "Id": 2,
        "Name": "B3",
        "myAs": [
            {
                "Id": 3,
                "Name": "A3"
            },
            {
                "Id": 4,
                "Name": "A4"
            },
            {
                "Id": 5,
                "Name": "A5"
            }
        ]
    }

Обратное, однако, возвращает странную иерархическую структуру:

    public IEnumerable<A> GetAs()
    {
        return _context.As.Include(a => a.myB).ToList();
    }

возвращает:

[
{
        "Id": 1,
        "Name": "A1",
        "myB": {
            "Id": 1,
            "Name": "B2",
            "myAs": [
                {
                    "Id": 2,
                    "Name": "A2"
                }
            ]
        }
    },
    {
        "Id": 2,
        "Name": "A2",
        "myB": {
            "Id": 1,
            "Name": "B2",
            "myAs": [
                {
                    "Id": 1,
                    "Name": "A1"
                }
            ]
        }
    },
    {
        "Id": 3,
        "Name": "A3",
        "myB": {
            "Id": 2,
            "Name": "B3",
            "myAs": [
                {
                    "Id": 4,
                    "Name": "A4"
                },
                {
                    "Id": 5,
                    "Name": "A5"
                }
            ]
        }
    }
]

Метод GetAs возвращает объекты A с объектами B с дальнейшей вложенностьюОбъекты.

Мое понимание после небольшого исследования (я мог бы быть здесь очень ошибочным) состоит в том, что, поскольку A имеет свойство навигации к B (myB), а B имеет свойство навигации к списку объектов A (myAs) это вызывает некоторую петлю.

Мои вопросы

  1. Правильно ли мое понимание здесь?Вот почему иерархия возвращается в этом странном макете?
  2. Как мне это исправить?Я могу взять свойство навигации ICollection из модели домена, но затем я больше не могу запрашивать As и связанные с ними B?

note Примечание A и B на самом деле не являются моими моделями домена.Я просто хотел сохранить пример как можно более простым.

Заранее спасибо.

1 Ответ

0 голосов
/ 04 октября 2018

Несколько вещей здесь:

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

Чтобы решить непосредственную проблему, настройте параметры сериализатора по умолчанию, например:

jsonFormatter.SerializerSettings.ReferenceLoopHandling  = Newtonsoft.Json.Serialization.ReferenceLoopHandling.Ignore;

Вышесказанное полезно при создании прототипов, но когда ваше приложение более формализовано, вы должны создатьи возвращать выделенные типы моделей представлений из ваших конечных точек веб-API.

public class AViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
}

public class BViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public IEnumerable<AViewModel> myAs { get; set; }
}

public IEnumerable<BViewModel> GetBs()
{
   return _context.Bs.Include(b => b.myAs)
        .Select(b => new BViewModel
        {
            Id = b.Id,
            Name = b.Name,
            As = b.As.Select(a => new AViewModel
            {
                 Id = a.Id,
                 Name = a.Name
            })
        })
        .ToList();
}

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

Лично я стараюсь максимально избегать подходов, основанных на отражении, поскольку они имеют тенденцию делать код сложным для рассуждения статически.Это мешает как читателям, таким как мы, так и инструментам, таким как язык C #.

Тем не менее, это может стоить компромисса в зависимости от поставленной задачи.Я надеюсь в конечном итоге увидеть поддержку на уровне языка, которая устраняет такие типовые назначения, не вступая в сферу жесткости, но мне еще долго ждать.

...