如何让Entity Framework在dotnet6.0的DbContext中使用Find/FindAsync检索嵌套对象。

huangapple go评论87阅读模式
英文:

How do you get Entity Framework to retrieve Nested Objects on Find/FindAsync from the DbContext on dotnet6.0

问题

  1. # 问题摘要
  2. ## 目标的详细信息
  3. 我想使用EFCoreSQLite从数据库中检索一个对象及其嵌套的对象成员。
  4. ## 预期结果与实际结果
  5. 目前,只有基本类型被正确实例化,而嵌套对象保持在默认/空状态,列表保持为空。
  6. 我期望的是EFCore将进行额外的调用并完全填充我的对象。
  7. ## 错误消息
  8. 没有抛出错误,只返回了垃圾数据。
  9. # 我尝试过的方法
  10. 1. 我尝试删除数据库,并希望使用EFCore重建,以解决可能在多次更新中出现的任何小问题。
  11. 2. 我还检查了数据库,以确保所有我需要的嵌套数据的条目都是有效的。
  12. 3. 我还尝试使用原始SQL,但在尝试使用表连接时会引发关于已引用某些内容的错误。
  13. # 简化的重现 #
  14. 我正在使用Microsoft.AspNetCore.Components.DataAnnotations.Validation 3.2.0-rc1.2023.4来访问ValidateComplexObjects
  15. ## FullName.cs
  16. ```csharp
  17. public class FullName
  18. {
  19. public ulong ID { get; set; }
  20. #region Name
  21. [Display(
  22. Name = "First Name",
  23. AutoGenerateField = true,
  24. AutoGenerateFilter = true,
  25. Description = "The first name of a person",
  26. GroupName = "Name",
  27. Order = 2, Prompt = "Enter first name here",
  28. ShortName = "FName")]
  29. [MinLength(3, ErrorMessage = "First name can't be shorter than 3 letters")]
  30. [StringLength(100)]
  31. [Required]
  32. public string FirstName { get; set; }
  33. [Display(
  34. Name = "Last Name",
  35. AutoGenerateField = true,
  36. AutoGenerateFilter = true,
  37. Description = "The last name of a person",
  38. GroupName = "Name",
  39. Order = 1, Prompt = "Enter last name here",
  40. ShortName = "LName")]
  41. [MinLength(3, ErrorMessage = "Last name can't be shorter than 3 letters")]
  42. [StringLength(100)]
  43. [Required]
  44. public string LastName { get; set; }
  45. [Display(
  46. Name = "Middle Name",
  47. AutoGenerateField = true,
  48. AutoGenerateFilter = true,
  49. Description = "The middle name of a person",
  50. GroupName = "Name",
  51. Order = 50, Prompt = "Enter middle name here",
  52. ShortName = "MName")]
  53. [MinLength(3, ErrorMessage = "Middle name can't be shorter than 3 letters")]
  54. [StringLength(100)]
  55. [Required]
  56. public string MiddleName { get; set; }
  57. public string Name => $"{FirstName} {MiddleName} {LastName}";
  58. #endregion
  59. }

Track.cs

  1. // 可以运行的轨道和它们的长度的数据
  2. public record Track(int Id, [Required] string Name, [Required]int distanceInMiles);

Person.cs

  1. public class Person{
  2. public ulong Id {get;set;}
  3. [ValidateComplexType] public FullName Name {get; set;}
  4. [Required] public string Description {get;set;}
  5. [ValidateComplexType] public List<Track> FriendsName {get;set;}
  6. }

ApplicationDbContext.cs

  1. public class ApplicationDbContext : IdentityDbContext
  2. {
  3. public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
  4. : base(options)
  5. {
  6. }
  7. public DbSet<FullName> Names { get; set; }
  8. public DbSet<Person> Persons { get; set; }
  9. public DbSet<Track> Tracks { get; set; }
  10. }

ViewPeople.razor

  1. @page "/Person/View/{id}"
  2. // 项目引用
  3. @inject ApplicationDbContext ApplicationDbContext
  4. @if(validId){ @*Blah Blah HTML*@ }
  5. else { "Invalid Id Requested" }
  6. @code {
  7. Person person;
  8. [Parameter]
  9. public string id { get; set; }
  10. bool validId = false;
  11. protected override async Task OnInitializedAsync()
  12. {
  13. await base.OnInitializedAsync();
  14. validId = ulong.TryParse(id, out var iId);
  15. person = await ApplicationDbContext.Persons.FindAsync(iId);
  16. validId = person is not null;
  17. }
  18. }

由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 "我不是一具尸体"

期望

我期望的

  1. {
  2. Id: 1,
  3. Name :
  4. {
  5. Id: 1,
  6. FirstName: "John",
  7. MiddleName: "Wittle",
  8. LastName: "Doe"
  9. },
  10. Description: "我不是一具尸体",
  11. Tracks = [
  12. {Id: 1, Name: "Honalulu", DistanceInMidles: 12km },
  13. {Id: 2, Name: "Honalulu 2", DistanceInMidles: 1km },
  14. {Id: 3, Name: "Honalulu 6", DistanceInMidles: 4km },
  15. ]
  16. }

我得到的

  1. {
  2. Id: 1,
  3. Name :
  4. {
  5. Id: 0,
  6. FirstName: null,
  7. MiddleName: null,
  8. LastName: null
  9. },
  10. Description: "我不是一具尸体",
  11. Tracks = []
  12. }
  1. <details>
  2. <summary>英文:</summary>
  3. # Summarization of Problem
  4. ## Details about goal
  5. I want to retrieve an object and its nested object members from the database using EFCore and SQLite.
  6. ## Expected and Actual Results
  7. 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.
  8. What I expect to happen is that EFCore will make the additional calls and fill my object completely.
  9. ## Error Messages
  10. No errors are thrown, just garbage data is returned.
  11. # What I have attempted
  12. 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.
  13. 2. I also checked the database to make sure there were valid entries to all the nested data that I required.
  14. 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.
  15. # Simplified Reproduction #
  16. I am using Microsoft.AspNetCore.Components.DataAnnotations.Validation 3.2.0-rc1.2023.4 to get access to ValidateComplexObjects.
  17. ## FullName.cs

public class FullName
{
public ulong ID { get; set; }

  1. #region Name
  2. [Display(
  3. Name = &quot;First Name&quot;,
  4. AutoGenerateField = true,
  5. AutoGenerateFilter = true,
  6. Description = &quot;The first name of a person&quot;,
  7. GroupName = &quot;Name&quot;,
  8. Order = 2, Prompt = &quot;Enter first name here&quot;,
  9. ShortName = &quot;FName&quot;)]
  10. [MinLength(3, ErrorMessage = &quot;First name can&#39;t be shorted than 3 letters&quot;)]
  11. [StringLength(100)]
  12. [Required]
  13. public string FirstName { get; set; }
  14. [Display(
  15. Name = &quot;Last Name&quot;,
  16. AutoGenerateField = true,
  17. AutoGenerateFilter = true,
  18. Description = &quot;The last name of a person&quot;,
  19. GroupName = &quot;Name&quot;,
  20. Order = 1, Prompt = &quot;Enter last name here&quot;,
  21. ShortName = &quot;LName&quot;)]
  22. [MinLength(3, ErrorMessage = &quot;Last name can&#39;t be shorted than 3 letters&quot;)]
  23. [StringLength(100)]
  24. [Required]
  25. public string LastName { get; set; }
  26. [Display(
  27. Name = &quot;Middle Name&quot;,
  28. AutoGenerateField = true,
  29. AutoGenerateFilter = true,
  30. Description = &quot;The middle name of a person&quot;,
  31. GroupName = &quot;Name&quot;,
  32. Order = 50, Prompt = &quot;Enter middle name here&quot;,
  33. ShortName = &quot;MName&quot;)]
  34. [MinLength(3, ErrorMessage = &quot;Middle name can&#39;t be shorted than 3 letters&quot;)]
  35. [StringLength(100)]
  36. [Required]
  37. public string MiddleName { get; set; }
  38. public string Name =&gt; $&quot;{FirstName} {MiddleName} {LastName}&quot;;
  39. #endregion

}

  1. ## 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);

  1. ## 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;}
}

  1. ## ApplicationDbContext.cs

public class ApplicationDbContext : IdentityDbContext
{
public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
: base(options)
{
}

  1. public DbSet&lt;FullName&gt; Names { get; set; }
  2. public DbSet&lt;Person&gt; Persons { get; set; }
  3. public DbSet&lt;Track&gt; Tracks { get; set; }

}

  1. ## ViewPeople.razor

@page "/Person/View/{id}"
// Project usings
@inject ApplicationDbContext ApplicationDbContext

@if(validId){ @Blah Blah HTML@ }
else { "Invalid Id Requested" }

@code {
Person person;

  1. [Parameter]
  2. public string id { get; set; }
  3. bool validId = false;
  4. protected override async Task OnInitializedAsync()
  5. {
  6. await base.OnInitializedAsync();
  7. validId = ulong.TryParse(id, out var iId);
  8. person = await ApplicationDbContext.Persons.FindAsync(iId);
  9. validId = person is not null;
  10. }

}

  1. ## What the table generated from EFCore looks like
  2. ### Name Table
  3. | id | FirstName | MiddleName | LastName |
  4. | -------- | -------------- |-------------- |-------------- |
  5. | 1 | John | Wittle | Door |
  6. ### Track Table
  7. id, Name, DistanceInMiles, PersonId
  8. | id | Name | DistanceInMiles | PersonId |
  9. | -------- | -------------- |-------------- |-------------- |
  10. | 1 | Honalulu | 12km | 1 |
  11. | 2 | Honalulu 2 | 1km | x |
  12. | 3 | Honalulu 6 | 4km | 1 |
  13. ### Person Table
  14. id, NameId, Description
  15. | id | NameId | Description |
  16. | -------- | -------------- |-------------- |-------------- |
  17. | 1 | 1 | &quot;I am not a cadaver&quot; |
  18. ### Expectations
  19. #### 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 },
]
}

  1. #### What I Get

{
Id: 1,
Name :
{
Id: 0,
FirstName: null,
MiddleName: null,
LastName: null
},
Description: "I am not a cadaver",
Tracks = []
}

  1. </details>
  2. # 答案1
  3. **得分**: 1
  4. EFCore会按照你要求的方式执行操作。请考虑你在"ViewPeople.razor"文件中的"OnInitializedAsync"方法。
  5. ```csharp
  6. protected override async Task OnInitializedAsync()
  7. {
  8. ..
  9. person = await ApplicationDbContext.Persons.FindAsync(iId);
  10. ..
  11. }

你要求EFCore只返回具有ID "iID" 的人员记录,没有其他内容。要包括相关实体,你需要明确告诉EFCore如何操作。在你的特定情况下,可以这样做:

  1. person = await ApplicationDbContext.Persons
  2. .Include(x => x.FriendsName)
  3. .FirstOrDefaultAsync(x => x.Id == iId);

如果你的跟踪实体还有其他依赖实体,可以链式调用Include,例如:

  1. person = await ApplicationDbContext.Persons
  2. .Include(x => x.FriendsName)
  3. .ThenInclude(x => x.DependentEntitiesOfTrack)
  4. .FirstOrDefaultAsync(x => x.Id == iId);
英文:

EFCore does exactly would you have asked it to do. Consider your 'OnInitializedAsync' in your 'ViewPeople.razor'.

  1. protected override async Task OnInitializedAsync()
  2. {
  3. ..
  4. person = await ApplicationDbContext.Persons.FindAsync(iId);
  5. ..
  6. }

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:

  1. person = await ApplicationDbContext.Persons
  2. .Include(x =&gt; x.FriendsName);
  3. .FirstOrDefaultAsync(x =&gt; x.Id == iId);

If your Track entity had another dependent entity, .Include-calls could also be chained, e.g.:

  1. person = await ApplicationDbContext.Persons
  2. .Include(x =&gt; x.FriendsName);
  3. .ThenInclude(x =&gt; x.DependentEntitiesOfTrack)
  4. .FirstOrDefaultAsync(x =&gt; x.Id == iId);

huangapple
  • 本文由 发表于 2023年5月26日 12:56:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/76337766.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定