EF Core 处理相同的实体,但一个是空的,一个不是
Posted
技术标签:
【中文标题】EF Core 处理相同的实体,但一个是空的,一个不是【英文标题】:EF Core Treating Entities the Same But One Is Null and One Is Not 【发布时间】:2021-12-27 13:32:19 【问题描述】:好的,大家好。我已经盯着这个看了好几个小时了,我觉得我快疯了。我知道这有点,很抱歉它太长了,我试图让它尽可能容易地流动,但是呃……让我把 tl;dr 放在前面。
tl;dr:对两个对象(EF Core 中的“实体”)进行相同的处理,但由于某种原因,它们的处理方式有所不同,这给了我一个非常烦人的错误。什么给了?
好的,首先我使用的是 Entity Framework 6.4.4、EntityFrameworkCore.Sqlite 和 EntityFrameworkCore.Tools 3.1.4、Visual Studio 16.10.0 和 .NET Core 3.1,但是,出于稍后您会看到的原因,我真的不认为这是相关的。我只是把它包括在内。
所以,这会有点,但我会尽量保持简单。我在访问数据时遇到问题,我不知道为什么。让我从我的模型开始。我将包括三个(并简化它们)
MyNamespace/Models/Project.cs
namespace MyNamespace.Models
public class Project
public int ID get; set;
// Bunch of other stuff, doesn't matter
public ICollection<Comment> Comments get; set;
public ICollection<MyNamespace.Models.File> Files get; set;
// Yes I'm intentionally using the full path here because this is where the confusion is
MyNamespace/Models/Comment.cs
namespace MyNamespace.Models
public class Comment
public int ID get; set;
public int ProjectID get; set;
// Other attributes
public Project Project get; set;
MyNamespace/Models/File.cs
namespace MyNamespace.Models
public class File
public int ID get; set;
public int ProjectID get; set;
// Other attributes
public MyNamespace.Models.Project Project get; set;
// Again being very explicit here because I'm at a loss
你看到我在这里得到了什么,对吧? Project
实体可以包含零个或多个 cmets 以及零个或多个文件,并且每个注释或文件可以与一个项目相关联。这是我们正在研究的一对多关系。不是超级复杂,我还有其他具有类似关系的实体,一切都像我期望的那样工作,除了File
实体。我在这里包括Comment
实体来说明,据我所知,我正在正确对待File
实体,与Comment
实体相同,但我不知道为什么它不起作用.不过,请注意,还有其他实体我也在处理同样的事情并且工作正常。
所以这些是有问题的模型。这是我的DbContext
MyNamespace/Data/ProjectDBContext.cs
namespace MyNamespace.Data
public class ProjectDBContext : DbContext
public DbSet<Project> Projects get; set;
// Other stuff
public DbSet<Comment> Comments get; set;
public DbSet<MyNamespace.Models.File> Files get; set;
同样,我有很多东西,一切都按预期工作,直到我添加了这个新的 File
模型。
所以,既然我们已经完成了所有设置,让我们进入代码吧。
我知道,我的组织有点奇怪,(但我认为这有点合适?),但我将包括所有我认为相关的内容。我正在使用视图组件和 Razor 页面,所以,首先,我们有一个常规的 Razor 页面(如果你们真的想要它,我会包含 MyNamespace/Pages/Projects/View.cshtml.cs
,但我真的不认为问题出在这里,所以我'现在要跳过它。)
MyNamepsace/Pages/Projects/View.cshtml
@model MyNamespace.Pages.Projects.View
<!-- Some stuff -->
<div>
@await Component.InvokeAsync("CommentsBox", Model.Project)
</div>
<div>
@await Component.InvokeAsync("FilesBox", Model.Project)
</div>
<!-- Other stuff -->
现在,我想澄清一下,Files
的东西不起作用。 Comments
的东西可以。我将两者都包括在内是为了说明它适用于一个而不是另一个,我不知道为什么。抱歉,我们的视图组件的第一部分。
MyNamespace/ViewComponents/CommentsBox.cs
namespace MyNamespace.ViewComponents
public class CommentsBox : ViewComponent
public IViewComponentResult Invoke(Project project)
return View("Default", project)
MyNamespace/ViewComponents/FilesBox.cs
namespace MyNamespace.ViewComponents
public class FilesBox : ViewComponent
public IViewComponentResult Invoke(Project project)
// We'll come back to this later, but I have a breakpoint set at this line.
return View("Default", project)
现在,观点(我实际上不知道当我称这些“观点”时我是否使用了正确的用语,但这不是重点)
MyNamespace/Pages/Shared/CommentsBox/Default.cshtml
@model MyNamespace.Models.Project
<!-- Some stuff -->
@foreach (var comment in Model.Comments)
// Just display info, this all works fine
<!-- Some stuff -->
MyNamespace/Pages/Shared/FilesBox/Default.cshtml
@model MyNamespace.Models.Project
<!-- Some stuff -->
@foreach (var file in Model.Files) // ERROR
// Supposed to display the info
<!-- Some stuff -->
<!-- Do note, there is some code here to upload a file. If I remove the code above that's throwing the error, It actually does upload the file. -->
所以我得到的错误是抱怨MyNamespace/Pages/Shared/FilesBox/Default.cshtml
中的for
循环,它是ArgumentNullException
。它抱怨Model.Files
为空,所以我无法对其进行迭代。但这没有任何意义,(你会看到它有一点作用,但我不知道为什么它是null
)。因为,对于我没有 cmets 和文件的项目,MyNamespace/Pages/Shared/CommentsBox/Default.cshtml
中的循环工作得很好。为什么它适用于 Comments
而不是 Files
?
此外,如果我从 MyNamespace/Pages/Shared/FilesBox/Default.cshtml
中删除 for 循环并使用上传文件的代码来上传文件,它就可以正常工作。完全没有问题。现在,我们知道我们在与该项目关联的数据库中有一个文件。所以,只是为了好玩,让我们改变MyNamespace/Pages/Shared/FilesBox/Default.cshtml
MyNamespace/Pages/Shared/FilesBox/Default.cshtml
@model MyNamespace.Models.Project
<!-- Some stuff -->
@try
@foreach (var file in Model.Files)
// We should display info here
catch (Exception ArgumentNullException)
<text>Woops we've caught our exception</text>
<!-- Some stuff -->
<!-- Do note, there is some code here to upload a file. It actually does upload the file, if I remove the code above that's throwing the error -->
好的,所以我们知道我们在数据库中有一个文件,我可以在数据库中看到它(而不是在网页上),我可以看到它的 ProjectID
正是我所期望的。再次运行它,我们发现了我们的错误情况。为什么?我们知道我们在数据库中有一个文件,其中 ProjectID
指向我们的项目。请记住,我们对 cmets 没有这个问题。即使数据库中没有带有此ProjectID
的 cmets,我们也不会收到此错误。然而,即使我们确实在数据库中有一个文件,我们仍然会收到错误,我们甚至可以通过询问错误条件来确认。
好的,所以,我们知道数据库中有一个文件指向我们的项目,但我们仍在输入错误条件。我将MyNamespace/Pages/Shared/FilesBox/Default.cshtml
改回原来的,没有错误检查。数据库保留其信息,无需担心。但是我们又回到了同样的错误。它只是没有将File
实体与其正确的Project
实体相关联。
还记得那个断点吗?这就是事情变得好奇的地方。如果我检查从MyNamepsace/Pages/Projects/View.cshtml
到MyNamespace/ViewComponents/FilesBox.cs
到MyNamespace/Pages/Shared/FilesBox/Default.cshtml
的Project
对象,就在它转到MyNamespace/Pages/Shared/FilesBox/Default.cshtml
之前,我看到了一些有趣的东西。我们可以在运行时的那个时刻检查对象,并看到确实,我们的Project
对象的Files
属性是null
,但是对于Comments
?不是null
,而是Count = 0
...所以...为什么?我觉得我为他们俩做了完全相同的事情。我以相同的方式设置它们,在DbContext
中对它们进行相同处理,在模型中对它们进行相同处理,以及它们与Project
的关联。为什么会有不同的待遇?
此外,如果我们在该断点检查Project
对象,我们可以看到它的Comments
属性,其中值是Count = 0
,类型是System.Collections.Generic.ICollection<MyNamespace.Models.Comment> System.Collections.Generic.HashSet<MyNamespace.Models.Comment>
。但是,对于我们的Project
对象的Files
属性,其值为null
(即使我们知道数据库中有ProjectID
指向该项目的文件),类型只是System.Collection.Generic.ICollection<MyNamespace.Models.File>
.我什至尝试将模型MyNamespace/Models/Project.cs
更改为
MyNamespace/Models/Project.cs
namespace MyNamespace.Models
public class Project
public int ID get; set;
// Bunch of other stuff, doesn't matter
public ICollection<Comment> Comments get; set;
public HashSet<MyNamespace.Models.File> Files get; set;
// See? I'm specifying the HashSet type here
但是,无济于事。同样的错误,同样的行为。
我确定我缺少的东西都很小,但我一直在寻找这个,当我把这整件事写出来时,已经有一天多的时间了,我只是不知所措。它是 Visual Studio 的东西吗?我错过了什么吗?我只是不知道。我希望你们这些优秀的人能够看到我看不到的东西,或者能够为我指明正确的方向。
【问题讨论】:
【参考方案1】:哇哦。还记得我说过,“如果你们真的想要的话,我会加入MyNamespace/Pages/Projects/View.cshtml.cs
,但我真的不认为问题出在这里,所以我现在要跳过它”?
嗯,我应该把它包括在内。没有追查她的对象的傻瓜有祸了。这是来自MyNamespace/Pages/Projects/View.cshtml.cs
的相关代码
MyNamespace/Pages/Projects/View.cshtml.cs
namespace MyNamespace.Pages.Projects
public class View : PageModel
private readonly IReposity _reposity;
public Project Project get; set;
public View (IRepository repository)
_repository = repository;
public async Task<IActionResult> OnGetAsync(int id)
// Some stuff
Project = _repository.GetOneProjectByIDAsNoTrackingAsync(id);
// Some other stuff
所以,你看,我正在使用存储库来访问我的数据,所以让我们看看该存储库中的相关方法。 叹息
MyNamespace/Repositories/Repository.cs
namespace MyNamespace.Repositories
private ProjectDBContext _context;
public Repository(ProjectDBContext context)
_context = context;
public async Task<Project> GetOneProjectByIDAsNoTrackingAsync(int id)
return await _context.Projects
.Include(p => p.Comments)
.Inlcude(p => p.SomethingElse)
// Include a bunch of other stuff
.ThenInclude(s => s.SomethingElseChild)
.AsNotracking()
.FirstOrDefaultAsync(m => m.ID == id);
你看,我从来没有在这里包含我的File
实体。哎呀,对吧?应该是
MyNamespace/Repositories/Repository.cs
namespace MyNamespace.Repositories
private ProjectDBContext _context;
public Repository(ProjectDBContext context)
_context = context;
public async Task<Project> GetOneProjectByIDAsNoTrackingAsync(int id)
return await _context.Projects
.Include(p => p.Comments)
.Inlcude(p => p.SomethingElse)
.Include(p => p.Files) // <== THIS THIS THIS
// Include a bunch of other stuff
.ThenInclude(s => s.SomethingElseChild)
.AsNotracking()
.FirstOrDefaultAsync(m => m.ID == id);
我写了整个 1,500 字的问题只是为了意识到我错过了什么。无论如何,我将把它留在那里并为后代回答这个问题。
【讨论】:
以上是关于EF Core 处理相同的实体,但一个是空的,一个不是的主要内容,如果未能解决你的问题,请参考以下文章