无论您是通过急切加载还是延迟加载来加载相关实体,都无法避免由 EF 填充反向导航属性。这种关系修复(正如@Colin 已经解释的那样)是一个你无法关闭的功能。
您可以通过在查询完成后取消不需要的反向导航属性来解决该问题:
foreach (var userTest in userTests)
{
if (userTest.Test != null)
{
userTest.Test.UserTests = null;
if (userTest.Test.Exam != null)
{
userTest.Test.Exam.Tests = null;
}
}
}
然而,在我看来,你的设计的缺陷是你试图序列化entities代替数据传输对象(“DTO”)专门用于要将数据发送到的视图。通过使用 DTO,您可以避免完全不需要的反向导航属性,以及视图中不需要的其他实体属性。您可以定义三个 DTO 类,例如:
public class ExamDTO
{
public int ExamId { get; set; }
public int SubjectId { get; set; }
public string Name { get; set; }
// no Tests collection here
}
public class TestDTO
{
public int TestId { get; set; }
public string Title { get; set; }
// no UserTests collection here
public ExamDTO Exam { get; set; }
}
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public TestDTO Test { get; set; }
}
然后使用投影来加载数据:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
Test = new TestDTO
{
TestId = ut.Test.TestId,
Title = ut.Test.Title,
Exam = new ExamDTO
{
ExamId = ut.Test.Exam.ExamId,
SubjectId = ut.Test.Exam.SubjectId,
Name = ut.Test.Exam.Name
}
}
})
.ToList();
您还可以通过仅定义一个包含视图所需的所有属性的 DTO 类来“展平”对象图:
public class UserTestDTO
{
public int UserTestId { get; set; }
public string UserId { get; set; }
public int QuestionsCount { get; set; }
public int TestId { get; set; }
public string TestTitle { get; set; }
public int ExamId { get; set; }
public int ExamSubjectId { get; set; }
public string ExamName { get; set; }
}
投影将变得更简单,如下所示:
var userTests = _uow.UserTests
.GetAll()
.Where(ut => ut.UserId == "0" || ut.UserId == userId)
.Select(ut => new UserTestDTO
{
UserTestId = ut.UserTestId,
UserId = ut.UserId,
QuestionsCount = ut.QuestionsCount,
TestId = ut.Test.TestId,
TestTitle = ut.Test.Title,
ExamId = ut.Test.Exam.ExamId,
ExamSubjectId = ut.Test.Exam.SubjectId,
ExamName = ut.Test.Exam.Name
})
.ToList();
通过使用 DTO,您不仅可以避免反向导航属性的问题,还可以遵循良好的安全实践,将数据库中公开的属性值显式“列入白名单”。假设您要添加一个测试访问权限Password
财产给Test
实体。使用序列化急切加载的具有所有属性的完整实体的代码,密码也会被序列化并通过网络运行。您无需更改任何代码即可发生这种情况,并且在最坏的情况下,您不会意识到您在 HTTP 请求中暴露了密码。另一方面,当您定义 DTO 时,如果您将此属性显式添加到 DTO 类,则新的实体属性只会使用您的 Json 数据进行序列化。