9 c# linq asp.net-mvc entity-framework asp.net-mvc-4
我想让具有UserTest实体的Exams和Test实体具有等于"0"的UserId或提供的值.我有很多建议,但到目前为止还没有任何建议.一个建议是从获取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();
Run Code Online (Sandbox Code Playgroud)
当我检查_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; }
}
Run Code Online (Sandbox Code Playgroud)
当我看到输出时,我看到这样的事情:
[{"userTestId":2,
"userId":"0",
"testId":12,
"test":{
"testId":12,"examId":1,
"exam":{
"examId":1,"subjectId":1,
"tests":[
{"testId":13,"examId":1,"title":"Sample Test1",
"userTests":[
{"userTestId":3,
"userId":"0",
Run Code Online (Sandbox Code Playgroud)
请注意,它获取一个UserTest对象,然后获取一个测试对象,然后获取一个考试对象.但是,检查对象包含一个测试集合,然后再次向下返回并获得不同的测试和单元测试:
UserTest> Test> Exam> Test> UserTest?
我已经努力确保延迟加载已关闭并且调试告诉我它已设置为false.我使用EF6和的WebAPI,但不知道是否有差别,因为我在调试C#的水平.
无论是否使用急切或延迟加载加载相关实体,都无法避免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;
}
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在我看来,您的设计的缺陷是您尝试序列化实体而不是数据传输对象("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; }
}
Run Code Online (Sandbox Code Playgroud)
然后使用投影加载数据:
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();
Run Code Online (Sandbox Code Playgroud)
您还可以通过仅定义包含视图所需的所有属性的单个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; }
}
Run Code Online (Sandbox Code Playgroud)
投影会变得更简单,看起来像这样:
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();
Run Code Online (Sandbox Code Playgroud)
通过使用DTO,您不仅可以避免反向导航属性的问题,还可以遵循良好的安全实践,明确地将数据库中公开的属性值"白名单".想象一下,您将向实体添加测试访问Password属性Test.使用您的代码序列化所有属性的热切加载的完整实体,密码也将被序列化并通过网络运行.您不必为此更改任何代码,在最坏的情况下,您不会意识到您在HTTP请求中公开密码.另一方面,在定义DTO时,如果将此属性显式添加到DTO类,则只能使用Json数据序列化新的实体属性.
| 归档时间: |
|
| 查看次数: |
1160 次 |
| 最近记录: |