英文:
How do you get Entity Framework to retrieve Nested Objects on Find/FindAsync from the DbContext on dotnet6.0
问题
# 问题摘要
## 目标的详细信息
我想使用EFCore和SQLite从数据库中检索一个对象及其嵌套的对象成员。
## 预期结果与实际结果
目前,只有基本类型被正确实例化,而嵌套对象保持在默认/空状态,列表保持为空。
我期望的是EFCore将进行额外的调用并完全填充我的对象。
## 错误消息
没有抛出错误,只返回了垃圾数据。
# 我尝试过的方法
1. 我尝试删除数据库,并希望使用EFCore重建,以解决可能在多次更新中出现的任何小问题。
2. 我还检查了数据库,以确保所有我需要的嵌套数据的条目都是有效的。
3. 我还尝试使用原始SQL,但在尝试使用表连接时会引发关于已引用某些内容的错误。
# 简化的重现 #
我正在使用Microsoft.AspNetCore.Components.DataAnnotations.Validation 3.2.0-rc1.2023.4来访问ValidateComplexObjects。
## FullName.cs
```csharp
public class FullName
{
public ulong ID { get; set; }
#region Name
[Display(
Name = "First Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The first name of a person",
GroupName = "Name",
Order = 2, Prompt = "Enter first name here",
ShortName = "FName")]
[MinLength(3, ErrorMessage = "First name can't be shorter than 3 letters")]
[StringLength(100)]
[Required]
public string FirstName { get; set; }
[Display(
Name = "Last Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The last name of a person",
GroupName = "Name",
Order = 1, Prompt = "Enter last name here",
ShortName = "LName")]
[MinLength(3, ErrorMessage = "Last name can't be shorter than 3 letters")]
[StringLength(100)]
[Required]
public string LastName { get; set; }
[Display(
Name = "Middle Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The middle name of a person",
GroupName = "Name",
Order = 50, Prompt = "Enter middle name here",
ShortName = "MName")]
[MinLength(3, ErrorMessage = "Middle name can't be shorter than 3 letters")]
[StringLength(100)]
[Required]
public string MiddleName { get; set; }
public string Name => $"{FirstName} {MiddleName} {LastName}";
#endregion
}
Track.cs
// 可以运行的轨道和它们的长度的数据
public record Track(int Id, [Required] string Name, [Required]int distanceInMiles);
Person.cs
public class Person{
public ulong Id {get;set;}
[ValidateComplexType] public FullName Name {get; set;}
[Required] public string Description {get;set;}
[ValidateComplexType] public List<Track> FriendsName {get;set;}
}
ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<FullName> Names { get; set; }
public DbSet<Person> Persons { get; set; }
public DbSet<Track> Tracks { get; set; }
}
ViewPeople.razor
@page "/Person/View/{id}"
// 项目引用
@inject ApplicationDbContext ApplicationDbContext
@if(validId){ @*Blah Blah HTML*@ }
else { "Invalid Id Requested" }
@code {
Person person;
[Parameter]
public string id { get; set; }
bool validId = false;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
validId = ulong.TryParse(id, out var iId);
person = await ApplicationDbContext.Persons.FindAsync(iId);
validId = person is not null;
}
}
由EFCore生成的表格的外观
Name 表格
id | FirstName | MiddleName | LastName |
---|---|---|---|
1 | John | Wittle | Door |
Track 表格
id, Name, DistanceInMiles, PersonId
id | Name | DistanceInMiles | PersonId |
---|---|---|---|
1 | Honalulu | 12km | 1 |
2 | Honalulu 2 | 1km | x |
3 | Honalulu 6 | 4km | 1 |
Person 表格
id, NameId, Description
id | NameId | Description | |
---|---|---|---|
1 | 1 | "我不是一具尸体" |
期望
我期望的
{
Id: 1,
Name :
{
Id: 1,
FirstName: "John",
MiddleName: "Wittle",
LastName: "Doe"
},
Description: "我不是一具尸体",
Tracks = [
{Id: 1, Name: "Honalulu", DistanceInMidles: 12km },
{Id: 2, Name: "Honalulu 2", DistanceInMidles: 1km },
{Id: 3, Name: "Honalulu 6", DistanceInMidles: 4km },
]
}
我得到的
{
Id: 1,
Name :
{
Id: 0,
FirstName: null,
MiddleName: null,
LastName: null
},
Description: "我不是一具尸体",
Tracks = []
}
<details>
<summary>英文:</summary>
# Summarization of Problem
## Details about goal
I want to retrieve an object and its nested object members from the database using EFCore and SQLite.
## Expected and Actual Results
Currently, only the basic types are being instantiated properly while the nested objects are left in a default/null state and lists are left empty.
What I expect to happen is that EFCore will make the additional calls and fill my object completely.
## Error Messages
No errors are thrown, just garbage data is returned.
# What I have attempted
1. I have tried to drop the database and rebuild with EFCore in hopes of fixing any minor problems that might have shown up over multiple updates.
2. I also checked the database to make sure there were valid entries to all the nested data that I required.
3. I also tried using raw sql but it would throw an error about something already being referenced before when trying to use table joining.
# Simplified Reproduction #
I am using Microsoft.AspNetCore.Components.DataAnnotations.Validation 3.2.0-rc1.2023.4 to get access to ValidateComplexObjects.
## FullName.cs
public class FullName
{
public ulong ID { get; set; }
#region Name
[Display(
Name = "First Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The first name of a person",
GroupName = "Name",
Order = 2, Prompt = "Enter first name here",
ShortName = "FName")]
[MinLength(3, ErrorMessage = "First name can't be shorted than 3 letters")]
[StringLength(100)]
[Required]
public string FirstName { get; set; }
[Display(
Name = "Last Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The last name of a person",
GroupName = "Name",
Order = 1, Prompt = "Enter last name here",
ShortName = "LName")]
[MinLength(3, ErrorMessage = "Last name can't be shorted than 3 letters")]
[StringLength(100)]
[Required]
public string LastName { get; set; }
[Display(
Name = "Middle Name",
AutoGenerateField = true,
AutoGenerateFilter = true,
Description = "The middle name of a person",
GroupName = "Name",
Order = 50, Prompt = "Enter middle name here",
ShortName = "MName")]
[MinLength(3, ErrorMessage = "Middle name can't be shorted than 3 letters")]
[StringLength(100)]
[Required]
public string MiddleName { get; set; }
public string Name => $"{FirstName} {MiddleName} {LastName}";
#endregion
}
## Track.cs
// Data on tracks that can be ranned on and how long they are
public record Track(int Id, [Required] string Name, [Required]int distanceInMiles);
## Person.cs
public class Person{
public ulong Id {get;set;}
[ValidateComplexType] public FullName Name {get; set;}
[Required] public string Description {get;set;}
[ValidateComplexType] public List<Track> FriendsName {get;set;}
}
## ApplicationDbContext.cs
public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}
public DbSet<FullName> Names { get; set; }
public DbSet<Person> Persons { get; set; }
public DbSet<Track> Tracks { get; set; }
}
## ViewPeople.razor
@page "/Person/View/{id}"
// Project usings
@inject ApplicationDbContext ApplicationDbContext
@if(validId){ @Blah Blah HTML@ }
else { "Invalid Id Requested" }
@code {
Person person;
[Parameter]
public string id { get; set; }
bool validId = false;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
validId = ulong.TryParse(id, out var iId);
person = await ApplicationDbContext.Persons.FindAsync(iId);
validId = person is not null;
}
}
## What the table generated from EFCore looks like
### Name Table
| id | FirstName | MiddleName | LastName |
| -------- | -------------- |-------------- |-------------- |
| 1 | John | Wittle | Door |
### Track Table
id, Name, DistanceInMiles, PersonId
| id | Name | DistanceInMiles | PersonId |
| -------- | -------------- |-------------- |-------------- |
| 1 | Honalulu | 12km | 1 |
| 2 | Honalulu 2 | 1km | x |
| 3 | Honalulu 6 | 4km | 1 |
### Person Table
id, NameId, Description
| id | NameId | Description |
| -------- | -------------- |-------------- |-------------- |
| 1 | 1 | "I am not a cadaver" |
### Expectations
#### What I expect
{
Id: 1,
Name :
{
Id: 1,
FirstName: "John",
MiddleName: "Wittle",
LastName: "Doe"
},
Description: "I am not a cadaver",
Tracks = [
{Id: 1, Name: "Honalulu", DistanceInMidles: 12km },
{Id: 2, Name: "Honalulu 2", DistanceInMidles: 1km },
{Id: 3, Name: "Honalulu 6", DistanceInMidles: 4km },
]
}
#### What I Get
{
Id: 1,
Name :
{
Id: 0,
FirstName: null,
MiddleName: null,
LastName: null
},
Description: "I am not a cadaver",
Tracks = []
}
</details>
# 答案1
**得分**: 1
EFCore会按照你要求的方式执行操作。请考虑你在"ViewPeople.razor"文件中的"OnInitializedAsync"方法。
```csharp
protected override async Task OnInitializedAsync()
{
..
person = await ApplicationDbContext.Persons.FindAsync(iId);
..
}
你要求EFCore只返回具有ID "iID" 的人员记录,没有其他内容。要包括相关实体,你需要明确告诉EFCore如何操作。在你的特定情况下,可以这样做:
person = await ApplicationDbContext.Persons
.Include(x => x.FriendsName)
.FirstOrDefaultAsync(x => x.Id == iId);
如果你的跟踪实体还有其他依赖实体,可以链式调用Include,例如:
person = await ApplicationDbContext.Persons
.Include(x => x.FriendsName)
.ThenInclude(x => x.DependentEntitiesOfTrack)
.FirstOrDefaultAsync(x => x.Id == iId);
英文:
EFCore does exactly would you have asked it to do. Consider your 'OnInitializedAsync' in your 'ViewPeople.razor'.
protected override async Task OnInitializedAsync()
{
..
person = await ApplicationDbContext.Persons.FindAsync(iId);
..
}
All your are asking EFCore to do, is to return the person record with ID 'iID' - nothing else. In order to include dependent entities you need to explicitly tell EFCore to do so. In your particular case:
person = await ApplicationDbContext.Persons
.Include(x => x.FriendsName);
.FirstOrDefaultAsync(x => x.Id == iId);
If your Track entity had another dependent entity, .Include-calls could also be chained, e.g.:
person = await ApplicationDbContext.Persons
.Include(x => x.FriendsName);
.ThenInclude(x => x.DependentEntitiesOfTrack)
.FirstOrDefaultAsync(x => x.Id == iId);
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论