Как работать только с моделями лесов Entity Framework из базы данных - PullRequest
0 голосов
/ 22 мая 2018

Я работал с базой данных .Net Core Entity Framework, используя метод Scaffolding.

Это породило у меня пару моделей / классов из моих таблиц базы данных, но сейчас я просто минимизирую проблемуЯ имею к этим двум таблицам ... отношение один ко многим в обоих столбцах ChampionID:

enter image description here

Итак, после создания лесов / картирования моделейс помощью инструментов EntityCore были сгенерированы следующие два класса (и несколько других, которые не имеют отношения):

Champion.cs:

public partial class Champion
{
    public Champion()
    {
        ChampionScreenshot = new HashSet<ChampionScreenshot>();
        ChampionUser = new HashSet<ChampionUser>();
        ChampionUserRate = new HashSet<ChampionUserRate>();
    }

    public int ChampionId { get; set; }
    public string Name { get; set; }
    public string Nickname { get; set; }
    public string Description { get; set; }
    public string ImagePath { get; set; }
    public byte AttackDamageScore { get; set; }
    public byte AbilityPowerScore { get; set; }
    public byte ResistanceScore { get; set; }
    public byte PlayingDifficult { get; set; }
    public int PrimaryClassId { get; set; }
    public int SecondaryClassId { get; set; }

    public ChampionClass PrimaryClass { get; set; }
    public ChampionClass SecondaryClass { get; set; }
    public ICollection<ChampionScreenshot> ChampionScreenshot { get; set; }
    public ICollection<ChampionUser> ChampionUser { get; set; }
    public ICollection<ChampionUserRate> ChampionUserRate { get; set; }
}

ChampionScreenshot.cs:

public partial class ChampionScreenshot
{
    public int ChampionScreenshotId { get; set; }
    public string ImagePath { get; set; }
    public int ChampionId { get; set; }

    public Champion Champion { get; set; }
}

Я сомневаюсь: как правильно получить объект Champion с заполненным атрибутом ChampionScreenshot?

Например, вот что я делаю на своем слое службы:

    public async Task<Champion> GetChampion(int id)
    {
        Champion champion = await _context.Champion.FirstAsync(m => m.ChampionId == id);
        champion.ChampionScreenshot = _context.ChampionScreenshot.ToListAsync().Result.FindAll(m => m.ChampionId == champion.ChampionId);

        return champion;
    } 

Таким образом, я в основном получаю определенного чемпиона, а затем заполняю атрибут ChampionScreenshot (который также является классом) отдельно, но дело в том, что внутри моего ChampionScreenshot также есть CАтрибут класса hampion, который полностью загружается еще раз:

enter image description here

, который, очевидно, генерирует ошибку, как только она обнаруживается в конечной точке Restful Service:

[Produces("application/json")]
[Route("api/Champions")]
public class ChampionsController : Controller
{
    [HttpGet("{id}")]
    public async Task<IActionResult> GetChampion([FromRoute] int id)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        var champion = await _service.GetChampion(id);

        if (champion == null)
        {
            return NotFound();
        }
        return Ok(champion);
    }
    ...

Ошибка:

Newtonsoft.Json.JsonSerializationException: Self referencing loop detected for property 'champion' with type 'ChampionsService.Models.Champion'. Path 'championScreenshot[0]'. 

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

1 Ответ

0 голосов
/ 22 мая 2018

Champion ссылается на себя:

Champion> несколько ChampionScreenshot> Champion (обратно к исходному объекту)

Это легко решить:

return Json(champion, new JsonSettings { ReferenceLoopHandling = ReferenceLoopHandling.Ignore });

Или выможет сделать это для всего приложения:

services.AddMvc().AddJsonOptions(opts => 
{
    opts.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});

А потом просто:

return Json(champion);

Хотя меня беспокоит следующее:

Champion champion = await _context.Champion.FirstAsync(m => m.ChampionId == id);
champion.ChampionScreenshot = _context.ChampionScreenshot.ToListAsync().Result.FindAll(m => m.ChampionId == champion.ChampionId);

Выговорят: «иди в базу данных, загрузи каждый снимок чемпионского экрана и найди те, которые мне нужны, с помощью поиска в памяти».Это не только ужасно медленно, но и тратит много ресурсов в вашем приложении и в базе данных.Для включения данных вы используете Include:

Champion champion = await _context.Champion
    .Include(x => x.ChampionScreenshot)
    .FirstAsync(x => x.ChampionId == id);

(здесь написано: «перейти в базу данных и привести меня к чемпиону, но также включить все соответствующие ChampionScreenshot через внутреннее соединение).

...