Entity Framework Core возвращает объект со многими отношениями ко многим - PullRequest
0 голосов
/ 07 сентября 2018

Я не уверен, как получить необходимую информацию, используя EF Core с приложением .NET Core. У меня есть две таблицы базы данных - OBJECTS и TAGS с отношением «многие ко многим» (используется третья таблица соединений OBJECTTAGS). Я хочу получить все объекты (около 1400) вместе с соответствующими тегами.

Это мои три модели:

using System;
using System.Collections.Generic;

namespace ContentMarketplace.Models
{
    public partial class Object
    {
        public int ObjectId { get; set; }
        ...
        public virtual List<ObjectTag> ObjectTags { get; set; }
    }
}

namespace ContentMarketplace.Models
{
    public partial class Tag
    {
        public int TagId { get; set; }
        ...
        public virtual List<ObjectTag> ObjectTags { get; set; }
    }
}

namespace ContentMarketplace.Models
{
    public partial class ObjectTag
    {
        public int ObjectId { get; set; }
        public virtual Object Object { get; set; }
        public int TagId { get; set; }
        public virtual Tag Tag { get; set; }
    }
}

И это то, что в методе OnModelCreating () в моем контексте:

modelBuilder.Entity<ObjectTag>(entity =>
{
    entity.HasKey(e => new { e.ObjectId, e.TagId });

    entity.HasOne(ot => ot.Object)
        .WithMany(o => o.ObjectTags)
        .HasForeignKey(ot => ot.ObjectId);

    entity.HasOne(ot => ot.Tag)
        .WithMany(t => t.ObjectTags)
        .HasForeignKey(ot => ot.TagId);
});

Эта проблема возникает, когда я пытаюсь вернуть данные в мой ObjectController.cs

namespace ContentMarketplace.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ObjectController : ControllerBase
    {
        private readonly ContentMarketplaceContext _context;

        public ObjectController(ContentMarketplaceContext context)
        {
            _context = context;
        }       

        [HttpGet]
        public ActionResult<List<ContentMarketplace.Models.Object>> GetAll()
        {
            return _context.Objects
                .Include(o => o.ObjectTags)
                .ThenInclude(ot => ot.Tag)
                .ToList();
        }
    }
}

Функция Include (). ThenInclude () создает циклическое отношение, в котором HTTP-запрос не только возвращает информацию о тегах, связанных с каждым объектом, но затем все объекты, связанные с каждым из этих тегов, и т. Д., Что приводит к сбою браузера.

Если я уберу ThenInclude (), он работает нормально, но не возвращает всю информацию о тегах, отсутствующую в моей модели ObjectTag.

Я знаю, что это связано с тем, что EF Core автоматически загружает материал, который уже находится в контексте (например, примечания к «подсказке» здесь https://docs.microsoft.com/en-us/ef/core/querying/related-data), но я не знаю, как еще вернуть JUST объект и теги без идти дальше.

Ответы [ 2 ]

0 голосов
/ 07 сентября 2018

Если вы используете Include или Theninclude в своем запросе, это создаст циклические ссылки. JSON не может обрабатывать циклические ссылки. Вы можете легко решить эту проблему, используя запрос Select.

Без DTO:

Напишите ваш GetAll() метод контроллера следующим образом:

[HttpGet]
public IActionResult GetAll()
{
    var objectList =  _context.Objects.Select(o => new
            {
               o.ObjectId,
               Tags = o.ObjectTags.Select(ot => ot.Tag).ToList()
            }).ToList();

     return Ok(objectList);
}

С DTO:

Напишите ваш класс DTO следующим образом:

public class ObjectDto
{
    public int ObjectId { get; set; }
    ....
    public List<Tag> Tags { get; set; }
}

Тогда ваш GetAll() метод контроллера должен быть следующим:

[HttpGet]
public ActionResult<List<ObjectDto>> GetAll()
{
    var objectList =  _context.Objects.Select(o => new ObjectDto
            {
               ObjectId = o.ObjectId,
               Tags = o.ObjectTags.Select(ot => ot.Tag).ToList()
            }).ToList();

     return objectList;
}

Примечание. Если вы используете Select внутри запроса, вам не нужно использовать Include или Theninclude.

Надеюсь, теперь он будет работать как положено!

0 голосов
/ 07 сентября 2018

Ваша проблема вызвана ссылкой на цикл, вы можете попытаться ниже игнорировать ссылку на цикл.

            services.AddMvc()
                .AddJsonOptions(opt => {
                    opt.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore;
                });

Для другого варианта вы можете попытаться вернуть ObjectModel с List<Tag> напрямую вместо List<ObjectTag>

ObjectModel

    public partial class ObjectModel
{
    public int ObjectId { get; set; }
    public string Name { get; set; }
    public virtual List<Tag> Tags { get; set; }
}

Запрос

        public List<Models.ObjectModel> GetAll()
    {
        //return _db.Object
        //    .Include(o => o.ObjectTags)
        //    .ThenInclude(ot => ot.Tag)
        //    .ToList();
        return _db.Object
            .Include(o => o.ObjectTags)
            .ThenInclude(ot => ot.Tag)
            .Select(r => new Models.ObjectModel
            {
                ObjectId = r.ObjectId,
                Name = r.Name,
                Tags = r.ObjectTags.Select(ot => ot.Tag).ToList()
            })
            .ToList();
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...