c# - 我的 Get 方法将某些字段返回为 null,而 GetById 方法返回所有内容

标签 c# asp.net-web-api

目前正在开发我的第一个 Web api 和我的第一个 C# 项目,我一直在尝试实现 Get 方法。

在我的项目中,用户可以拥有职业(记者、开发人员等)和职业领域(体育、商业、网站开发等)。

当我尝试获取所有用户时,职业 (UserProfession) 和职业字段 (UserProfessionField) 属性返回为 null。当我尝试仅通过 id 返回一个用户时,这些相同的属性不再为 null。

我有以下用户模型:

namespace Sims.Models
{
    public partial class User
    {
        public User()
        {
            DataUsages = new HashSet<DataUsage>();
        }

        public long IdUser { get; set; }
        public int UserProfessionId { get; set; }
        public int UserProfessionFieldId { get; set; }
        public string? UserName { get; set; }
        public string? UserMail { get; set; }
        public string? UserCompany { get; set; }
        public byte[]? UserPicture { get; set; }

        public virtual Profession UserProfession { get; set; } = null!;
        public virtual ProfessionField UserProfessionField { get; set; } = null!;
        public virtual ICollection<DataUsage> DataUsages { get; set; }
    }
}

这是我用于此模型的 DTO:

namespace sims.DTO
{
    public partial class UserDTO
    {
        public long IdUser { get; set; }
        public string? UserName { get; set; }
        public string? UserMail { get; set; }
        public string? UserCompany { get; set; }
        public virtual ProfessionDTO UserProfession { get; set; } = null!;
        public virtual ProfessionFieldDTO UserProfessionField { get; set; } = null!;
    }
}

然后是我的 Get 方法及其结果:

[HttpGet]
public async Task<ActionResult<IEnumerable<UserDTO>>> GetUsers()
{
    var users = _context.Users.ToList();
    var userDtos = new List<UserDTO>();
    foreach (var user in users)
    {
        userDtos.Add(new UserDTO 
        { 
            IdUser = user.UserProfessionId, 
            UserName = user.UserName, 
            UserCompany = user.UserCompany, 
            UserMail = user.UserMail, 
            UserProfession =  user.UserProfession == null ?
            null as ProfessionDTO : new ProfessionDTO
            {
                IdProfession = user.UserProfession.IdProfession,
                ProfessionName = user.UserProfession.ProfessionName
            },
            UserProfessionField = user.UserProfessionField == null ?
            null as ProfessionFieldDTO : new ProfessionFieldDTO
            {
                IdProfessionField = user.UserProfessionField.IdProfessionField,
                ProfessionFieldName = user.UserProfessionField.ProfessionFieldName
            }
        });
    }
    return userDtos;
}
[
  {
    "idUser": 2,
    "userName": "user_test",
    "userMail": "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="117c70787d51657462653f727e7c" rel="noreferrer noopener nofollow">[email protected]</a>",
    "userCompany": "TestCompany",
    "userProfession": null,
    "userProfessionField": null
  },
  {
    "idUser": 1,
    "userName": "roger_federer",
    "userMail": "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="34525150514674525150514c1a575c" rel="noreferrer noopener nofollow">[email protected]</a>",
    "userCompany": null,
    "userProfession": null,
    "userProfessionField": null
  },
  {
    "idUser": 3,
    "userName": "rafa_nadal",
    "userMail": null,
    "userCompany": null,
    "userProfession": null,
    "userProfessionField": null
  }
]

以及 GetById 方法及其结果:

        [HttpGet("{id}")]
        public async Task<ActionResult<UserDTO>> GetUserById(long id)
        {
            UserDTO User = await _context.Users.Select(user => new UserDTO
            {
                IdUser = user.UserProfessionId,
                UserName = user.UserName,
                UserCompany = user.UserCompany,
                UserMail = user.UserMail,
                UserProfession = user.UserProfession == null ?
                    null as ProfessionDTO : new ProfessionDTO
                    {
                        IdProfession = user.UserProfession.IdProfession,
                        ProfessionName = user.UserProfession.ProfessionName
                    },
                UserProfessionField = user.UserProfessionField == null ?
                    null as ProfessionFieldDTO : new ProfessionFieldDTO
                    {
                        IdProfessionField = user.UserProfessionField.IdProfessionField,
                        ProfessionFieldName = user.UserProfessionField.ProfessionFieldName
                    }
            }).FirstOrDefaultAsync(user => user.IdUser == id);
            if(User == null)
            {
                return NotFound();
            }
            else
            {
                return User;
            }
        }
{
  "idUser": 1,
  "userName": "roger_federer",
  "userMail": "<a href="https://stackoverflow.com/cdn-cgi/l/email-protection" class="__cf_email__" data-cfemail="2f494a4b4a5d6f494a4b4a57014c47" rel="noreferrer noopener nofollow">[email protected]</a>",
  "userCompany": null,
  "userProfession": {
    "idProfession": 1,
    "professionName": "Journalist"
  },
  "userProfessionField": {
    "idProfessionField": 1,
    "professionFieldName": "Sports"
  }
}

您知道此问题的根源是什么吗?

最佳答案

在您的数据模型中,UserProfessionUserProfessionField 存储在单独的表中;对于这两者,您的 User 类上都有所谓的导航属性。使用 Entity Framework 时,这些导航属性可以是急切加载和延迟加载的。前者直接从父对象获取值,后者按需获取。

GetUsers 方法中,第一条语句使用 ToList 从数据库加载所有数据 - 这会破坏延迟加载的能力。只有在此之后,才会使用 Select 进行投影,但此时数据无法再延迟加载。

GetUserById 相比,对 FirstOrDefaultAsync 的调用遵循 Select 的投影。在Select期间,通过访问UserProfession(Field)属性,您可以延迟加载其他表中的相应数据。

这解释了为什么会发生这种行为。为了解决这个问题,出于性能原因,您应该从延迟加载更改为急切加载(如果您加载 20 个用户并为每个用户加载依赖数据,您将有 41 个请求,而使用急切加载,您只需要一个)。您可以使用 Include 方法来实现此目的:

[HttpGet]
public async Task<ActionResult<IEnumerable<UserDTO>>> GetUsers()
{
  var users = _context.Users
    .Include(x => x.UserProfession)
    .Include(x => x.UserProfessionField);
  // ...

以上代码在加载Users时直接包含UserProfessionUserProfessionField表中的信息。

此外,您还可以更改 GetUserById 以包含这些属性 - 尽管性能问题并不像加载列表那样严重。

关于c# - 我的 Get 方法将某些字段返回为 null,而 GetById 方法返回所有内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73957496/

相关文章:

c# - 到根 URL 的 WebAPI 路由

c# - 是否可以从 C# WebApi 重用/调用 C# 网站中的处理程序?

c# - 过载分辨率异常

c# - ASP.NET MVC 页面发布后速度很慢

asp.net - System.Web.Http.Authorize 与 System.Web.Mvc.Authorize

c# - 通过 ASP.NET Web API 有效地使用 async/await

c# - 计算 2 个列表中存在的项目

c# - 构建失败。检查“输出”窗口以获取更多详细信息 - C# 发布失败但构建成功

c# - HttpClientFactory BadRequest?

c# - REST API 放大数据