【问题标题】:Entity Framework Core returning object with many to many relationshipEntity Framework Core 返回具有多对多关系的对象
【发布时间】:2018-09-06 21:14:47
【问题描述】:

我不确定如何通过 .NET Core 应用程序使用 EF 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),但我不知道还有什么方法可以返回对象和标签而不执行任何操作进一步。

【问题讨论】:

  • 错误是来自 JSON 序列化程序还是来自 EF Core?

标签: c# asp.net-core entity-framework-core


【解决方案1】:

如果您在查询中使用IncludeTheninclude,它将创建循环引用。 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,则无需使用IncludeTheninclude

希望它现在能按预期工作!

【讨论】:

  • JSON 可以处理循环引用。您需要将其配置为忽略它们。请参阅@TaoZhou 的回答。
  • @Brad 但是你已经指定了一个特定的层次结构,否则你的浏览器会崩溃。此外,如果您使用Select,您甚至不需要使用 Include 并设置其他设置来忽略 Reference Loop。
  • Select 语句的方式有效,但有没有更简单的方法,比如使用 Mapster/AutoMapper?手动填充 DTO 有点繁琐。
  • @blankface AutoMapper 不好用。连 AutoMapper 的作者都说不应该用。
  • @zoran,你能给我链接吗?
【解决方案2】:

您的问题是由循环引用引起的,您可以尝试在下面忽略循环引用。

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

对于另一种选择,您可以尝试直接返回 ObjectModelList&lt;Tag&gt; 而不是 List&lt;ObjectTag&gt;

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();
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-09
    • 2021-04-09
    • 2016-12-18
    • 1970-01-01
    相关资源
    最近更新 更多