c# – 为什么我的代码执行懒惰加载,即使我把它关闭在所有可能的点?

我想获得具有UserId的等级为“0”的UserTest实体的考试和测试实体或提供的值.我有一些建议,但到目前为止还没有工作.有一个建议是从获取UserTest数据开始,另一个解决方案是从获取考试数据开始.当我使用UserTests作为源起点时,这是我所用的.

我有以下LINQ:

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.

这是我的课程:

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; }
}

当我看着输出时,我看到这样的东西:

[{"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解释)是一个您无法关闭功能.

您可以通过在查询完成后取消未完成的反向导航属性解决问题:

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类,例如:

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,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数据序列化.

相关文章

在要实现单例模式的类当中添加如下代码:实例化的时候:frmC...
1、如果制作圆角窗体,窗体先继承DOTNETBAR的:public parti...
根据网上资料,自己很粗略的实现了一个winform搜索提示,但是...
近期在做DSOFramer这个控件,打算自己弄一个自定义控件来封装...
今天玩了一把WMI,查询了一下电脑的硬件信息,感觉很多代码都...
最近在研究WinWordControl这个控件,因为上级要求在系统里,...