我想获得具有UserId的等级为“0”的UserTest实体的考试和测试实体或提供的值.我有一些建议,但到目前为止还没有工作.有一个建议是从获取UserTest数据开始,另一个解决方案是从获取考试数据开始.当我使用UserTests作为源起点时,这是我所用的.
我有以下LINQ:
@H_404_4@var userTests = _uow.UserTests .GetAll() .Include(t => t.Test) .Include(t => t.Test.Exam) .Where(t => t.UserId == "0" || t.UserId == userId) .ToList();当我使用调试器检查_uow.UserTests时,它是一个存储库,当我检查dbcontext的configuration.lazyloading时,它被设置为false.
这是我的课程:
@H_404_4@public class Exam { public int ExamId { get; set; } public int SubjectId { get; set; } public string Name { get; set; } public virtual ICollection<Test> Tests { get; set; } } public class Test { public int TestId { get; set; } public int ExamId { get; set; } public string Title { get; set; } public virtual ICollection<UserTest> UserTests { get; set; } } public class UserTest { public int UserTestId { get; set; } public string UserId { get; set; } public int TestId { get; set; } public int QuestionsCount { get; set; } }当我看着输出时,我看到这样的东西:
@H_404_4@[{"userTestId":2,"userId":"0","testId":12,"test":{ "testId":12,"examId":1,"exam":{ "examId":1,"subjectId":1,"tests":[ {"testId":13,"title":"Sample Test1","userTests":[ {"userTestId":3,请注意,它获取一个UserTest对象,然后获取一个测试对象,然后获取一个考试对象.然而,考试对象包含一个测试集合,然后再次返回,并获得不同的测试和单元测试:
UserTest>测试>考试>测试> UserTest?
我已经努力确保懒惰加载是关闭和调试告诉我它被设置为假.我正在使用EF6和WebAPI,但不知道如果这是有区别的,因为我正在调试C#级别.
解决方法
您不能避免反向导航属性由EF填充,无论您是否加载相关的实体加载或加载.这个关系fixup(已经由@Colin解释)是一个您无法关闭的功能.
@H_404_4@foreach (var userTest in userTests)
{
if (userTest.Test != null)
{
userTest.Test.UserTests = null;
if (userTest.Test.Exam != null)
{
userTest.Test.Exam.Tests = null;
}
}
}
然而,在我看来,您的设计的缺陷是您尝试将实体序列化,而不是将数据专用于要发送数据的视图的数据传输对象(“DTO”).通过使用DTO,您可以避免不需要的反向导航属性,也可以避免您在视图中不需要的其他实体属性.您将定义三个DTO类,例如:
@H_404_4@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; } }然后使用投影来加载数据:
@H_404_4@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类来“展平”对象图:
@H_404_4@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; } }投影将变得更简单,如下所示:
@H_404_4@var userTests = _uow.UserTests .GetAll() .Where(ut => ut.UserId == "0" || ut.UserId == userId) .Select(ut => new UserTestDTO { UserTestId = ut.UserTestId,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,您不仅可以避免反向导航属性的问题,还可以遵循良好的安全措施,从而明确地从数据库中“显示”暴露的属性值.想象一下,您将向测试实体添加测试访问密码属性.使用您的代码,将所有属性的加载完整实体序列化,密码也将被序列化并运行在线上.您不需要更改任何代码来实现,在最坏的情况下,您不会意识到您在HTTP请求中泄露密码.另一方面,当您定义DTO时,如果将此属性显式添加到DTO类,则新实体属性将仅与Json数据序列化.