【问题标题】:Asp.Net Core / Entity Framework Core - System.Linq.Enumerable+SelectEnumerableIterator`2[MyClass,System.String]Asp.Net Core / Entity Framework Core - System.Linq.Enumerable+SelectEnumerableIterator`2[MyClass,System.String]
【发布时间】:2020-11-25 17:15:27
【问题描述】:

我正在尝试返回我的 3 级深度相关数据的 json 数组,这里的问题是我得到的结果具有正确的属性名称但值内容不明确,我找不到类似的情况来解决它。从返回的值消息看来,我正在返回一个可查询的而不是最终结果,我需要对其进行迭代,我尝试了几种方法来实现它,但未能找到正确的方法。

json结果:

[
{
"registeredYear": "System.Linq.Enumerable+SelectEnumerableIterator`2[MyPath.Groups.GroupYear,System.String]"
}
]

api 端点

public async Task<ActionResult<IEnumerable<UserGroup>>> GetUserGroupYears(string email, string groupName)
    {
        var groupYears = await _repo.GetUserGroupYears(email, groupName);
        var mappedEntities = _mapper.Map<GroupYearsListDto[]>(groupYears);
        return Ok(mappedEntities);
    }

存储库方法

public async Task<IEnumerable<UserGroup>> GetUserGroupYears(string email, string groupName)
{
    var userGroupYears = _context.UserGroups
                          .Include(uo => uo.Group.GroupYears)
                            .ThenInclude( oy => oy.Year)
                          .Where(uo => uo.Group.Name == groupName && uo.Email == email );
            return await userGoupYears.ToArrayAsync();
}

使用的类:

public class UserGroup
{
    public string Email { get; set; }
    
    public string UserId { get; set; }
    public virtual User User { get; set; }
    
    public string GroupId { get; set; }
    public virtual Group Group { get; set; }
}

public class Group
{
    public string Name { get; set; }
    public virtual ICollection<UserGroup> Users { get; set; }
    public virtual ICollection<GroupYear> GroupYears { get; }
}

public class GroupYear    {
    public string GroupId { get; set; }
    public virtual Group Group { get; set; }

    public string YearId { get; set; }
    public virtual Year Year { get; set; }
    public string RegisteredYear { get; set; }
}

数据传输对象和映射:

public class GroupYearsListDto
{
    public string RegisteredYear { get; set; }
}
public CoreMappingProfiles()
{
    CreateMap<UserGroup, GroupYearsListDto>()
      .ForMember(
          dest => dest.RegisteredYear,
          opt => opt.MapFrom(src => src.Group.GroupYears.Select(x => x.RegisteredYear))
          );                  
}

更新:附加调试器表明存储库方法正在返回一个包含正确值的 IQueryable,并且控制器方法在映射时出错。所以我认为下面这行是造成错误结果的原因:

var mappedEntities = _mapper.Map<GroupYearsListDto[]>(GroupYears);

【问题讨论】:

  • 首先,Select(x =&gt; x.RegisteredYear) 提供了一个 IEnumerable&lt;string&gt;,您将其映射到 string - 这不起作用。其次,尝试Task&lt;UserGroup[]&gt; GetUserGroupYears - 无需在那里返回IEnumerable
  • 好的,但是OkObjectResult无法转换为Task
  • 您的控制器可以简单地是public ActionResult&lt;UserGroup[]&gt; GetUserGroupYears。在那里返回Task 是没有意义的。
  • 在同一个控制器中,我有另一种可以正常工作的方法: public async Task>> GetUserGroups 我可以删除 IEnumerable 但不能删除 Task,如果我先删除 Task需要使方法同步,然后它会遇到映射问题。
  • 我更新了问题,请看一下。

标签: linq asp.net-core entity-framework-core automapper


【解决方案1】:

你得到这个 JSON 结果:

[
  {
    "registeredYear": "System.Linq.Enumerable+SelectEnumerableIterator`2[MyPath.Groups.GroupYear,System.String]"
  }
]

因为您将IEnumerable&lt;string&gt; 映射到string,正如我在评论中提到的那样。所以基本上你得到的是相同的:

CreateMap<UserGroup, GroupYearsListDto>()
    .ForMember(
        dest => dest.RegisteredYear,
        opt => opt.MapFrom(src =>
        {
            IEnumerable<string> registeredYears = src.Group.GroupYears.Select(x => x.RegisteredYear);
            return registeredYears.ToString();
        })
    );

registeredYears.ToString()"System.Linq.Enumerable+SelectEnumerableIterator`2[MyPath.Groups.GroupYear,System.String]"

我想你会:

  1. 只有一个 - 所以请执行以下操作:src.Group.GroupYears.Select(x =&gt; x.RegisteredYear).Single()
  2. 有倍数 - 所以请执行以下操作:string.Join(", ", src.Group.GroupYears.Select(x =&gt; x.RegisteredYear))

您有很多选择,但您实际上需要将string 返回到该属性,否则您将只获得IEnumerable&lt;string&gt;ToString() 版本。

更新:

根据你下面的 cmets,你可以试试这个:

存储库:

public IQueryable<GroupYear> GetGroupYears(string email, string groupName)
{
    return _context
        .UserGroups
        .Where(x => x.Group.Name == groupName && x.Email == email)
        .SelectMany(x => x.Group.GroupYears);
}

控制器:

public async Task<ActionResult<GroupYearsListDto[]>> GetGroupYears(string email, string groupName)
{
    var groupYears = _repo.GetGroupYears(email, groupName);
    var projection = _mapper.ProjectTo<GroupYearsListDto>(groupYears)
    var mappedEntities = await projection.ToArrayAsync();
    return Ok(mappedEntities);
}

简介:

CreateMap<GroupYears, GroupYearsListDto>();

【讨论】:

  • 我有多个,所以尝试了第二个选项并且它起作用了,你能澄清一下这个 string.Join() 在这里做了什么吗?。
  • AutoMapper 正在尝试从源 (src) 映射到目标 (dest),即 string。如果您提供的源不是string,AutoMapper 将尝试通过调用ToString() 来强制它 - 这就是发生在您身上的事情。因此,通过将 IQueryable 实现为多个 strings 然后将它们连接在一起,您可以正确地提供 AutoMapper 可以成功映射到 string 目标的单个 string 源。
  • string.Join(", ", sourceValue) 在这种情况下不是正确的函数,因为它以逗号分隔的唯一字符串显示值,如下所示: [{ "registeredYear": "2018, 2019, 2020" }] 虽然它们应该是这样的数组: [{ "registeredYear": "2018", },{ "registeredYear": "2019" }, ... ] 以获得最佳 json 结果。请检查:页面底部的docs.automapper.org/en/stable/Lists-and-arrays.html
  • 这是一个很好的解决方法,因为很难找到一种方法来使用 automapper 映射更深的集合属性,我已经尝试过你的解决方案,它干净简洁,而且效果很好,所以我会接受它作为答案,谢谢您的帮助。
  • 顺便说一句:这两种方法(来自 repo 和来自控制器)可以在控制器中合并为一个方法。
猜你喜欢
  • 2017-05-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-14
  • 2018-09-28
  • 2018-11-05
  • 2018-05-24
  • 2019-02-23
相关资源
最近更新 更多