目前正在开发我的第一个 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"
}
}
您知道此问题的根源是什么吗?
最佳答案
在您的数据模型中,UserProfession
和 UserProfessionField
存储在单独的表中;对于这两者,您的 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
时直接包含UserProfession
和UserProfessionField
表中的信息。
此外,您还可以更改 GetUserById
以包含这些属性 - 尽管性能问题并不像加载列表那样严重。
关于c# - 我的 Get 方法将某些字段返回为 null,而 GetById 方法返回所有内容,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73957496/