C# 使用LINQ to实体对多级子集合进行排序
考虑以下模型:C# 使用LINQ to实体对多级子集合进行排序,c#,entity-framework,linq,sorting,C#,Entity Framework,Linq,Sorting,考虑以下模型: public class Form { public Guid Id { get; set; } public List<Section> Sections { get; set; } } public class Section { public Guid Id { get; set; }
public class Form
{
public Guid Id
{
get;
set;
}
public List<Section> Sections
{
get;
set;
}
}
public class Section
{
public Guid Id
{
get;
set;
}
public List<Question> Questions
{
get;
set;
}
public int SortOrder
{
get;
set;
}
}
public class Question
{
public Guid Id
{
get;
set;
}
public int SortOrder
{
get;
set;
}
}
从那里,我可以通过使用以下方法获得实际的表单
对象:
var form = query.ToList().Select(q => q.Root).FirstOrDefault();
我无法理解的是如何编写LINQ查询以将此行为扩展到第二级集合(每个部分对象中问题对象的集合)
*更新*
请参阅下面我对Ivan的评论,其中解释了这个问题如何不重复。要实现您的需求,您可以使用:
扩展方法允许您将与查询相关的实体包括在其中,甚至包括深层次的实体(请查看上面我引用的备注部分)
第二种解决方案可能是使用,如果您没有禁用此功能(默认情况下为启用),则需要满足某些条件才能使用,例如,您的导航属性必须是virtual
更新
您也可以在内存中对导航属性进行排序,如@ IvStoev引用的解决方案,但是如果您想将相关实体按顺序排序、过滤,在其他操作中,可以考虑使用:
但IMHO最好的解决方案是创建自定义类(也称为)以投影所需的结果,只加载一次往返所需的数据,以实现您可以使用的目标:
扩展方法允许您将与查询相关的实体包括在其中,甚至包括深层次的实体(请查看上面我引用的备注部分)
第二种解决方案可能是使用,如果您没有禁用此功能(默认情况下为启用),则需要满足某些条件才能使用,例如,您的导航属性必须是virtual
更新
您也可以在内存中对导航属性进行排序,如@ IvStoev引用的解决方案,但是如果您想将相关实体按顺序排序、过滤,在其他操作中,可以考虑使用:
但最好的解决方案是创建自定义类(也称为)来投影所需的结果,在一次往返中只加载所需的数据记住,您是在直接查询整个表集,那么您就不需要使用方法.Include()
快速加载
下面是一个lambda表达式方法,通过显式映射属性/列来解决此问题
// TODO: replace the null value with a real context DbSet<Form>
IQueryable<Form> forms = null;
var form = forms.Select(x => new Form()
{
Id = x.Id,
Sections = x.Sections.OrderBy(s => s.SortOrder).Select(s => new Section()
{
Id = s.Id,
SortOrder = s.SortOrder,
Questions = s.Questions.OrderBy(q => q.SortOrder).Select(q => new Question()
{
Id = q.Id,
SortOrder = q.SortOrder
}).ToList()
}).ToList()
}).FirstOrDefault();
//TODO:用实际上下文DbSet替换空值
IQueryable forms=null;
var form=forms.Select(x=>newform()
{
Id=x.Id,
Sections=x.Sections.OrderBy(s=>s.SortOrder)。选择(s=>newsection()
{
Id=s.Id,
SortOrder=s.SortOrder,
Questions=s.Questions.OrderBy(q=>q.SortOrder)。选择(q=>newquestion()
{
Id=q.Id,
SortOrder=q.SortOrder
})托利斯先生()
})托利斯先生()
}).FirstOrDefault();
请记住,您直接查询的是整个表集,因此不需要使用方法.Include()
立即加载
下面是一个lambda表达式方法,通过显式映射属性/列来解决此问题
// TODO: replace the null value with a real context DbSet<Form>
IQueryable<Form> forms = null;
var form = forms.Select(x => new Form()
{
Id = x.Id,
Sections = x.Sections.OrderBy(s => s.SortOrder).Select(s => new Section()
{
Id = s.Id,
SortOrder = s.SortOrder,
Questions = s.Questions.OrderBy(q => q.SortOrder).Select(q => new Question()
{
Id = q.Id,
SortOrder = q.SortOrder
}).ToList()
}).ToList()
}).FirstOrDefault();
//TODO:用实际上下文DbSet替换空值
IQueryable forms=null;
var form=forms.Select(x=>newform()
{
Id=x.Id,
Sections=x.Sections.OrderBy(s=>s.SortOrder)。选择(s=>newsection()
{
Id=s.Id,
SortOrder=s.SortOrder,
Questions=s.Questions.OrderBy(q=>q.SortOrder)。选择(q=>newquestion()
{
Id=q.Id,
SortOrder=q.SortOrder
})托利斯先生()
})托利斯先生()
}).FirstOrDefault();
根据我的经验,您实际上不需要像您提到的那样发出包含调用。我在上面发布的LINQ查询(没有包含调用)的工作原理是一样的。也就是说,正如我上面提到的,我能够获得第一级排序(对部分
对象进行排序)。我需要在下一级进行排序(问题
给定部分中的对象
对象)。但是,您能否再解释一下,您需要如何在查询结果中投影问题集合?就像匿名类型中的新属性一样?使用上面列出的模型,我希望得到一个单独的表单
对象,该对象的部分
集合已排序。在每个部分中
对象s集合,我希望对其问题集合进行排序。我最初发布的内容和您发布的内容将完成此要求的前半部分,但不会完成后半部分。@JasonRichmeier,请用新的更改检查我的查询。是的,包含无效。投影:1.使EF忽略它,2.本身定义加载的子实体。不过,这是实现OP所需的方法,尤其是当投影转换为形式
对象时(应用AEnumerable
后)。使用EF完成这些事情总是有点麻烦。根据我的经验,您实际上不需要像您提到的那样发出包含调用。我在上面发布的LINQ查询(没有包含调用)的工作原理相同。也就是说,正如我上面提到的,我能够获得第一级排序(对部分
对象进行排序)进行排序。我需要在下一个级别进行排序(问题
给定部分
对象内的对象).但是,您能否再解释一下,您需要如何在查询结果中投影问题集合?就像匿名类型中的新属性一样?使用上面列出的模型,我想得到一个表单foreach f in context.Form
{
context.Entry(f).Collection(r => r.Sections)
.Query().OrderBy(s=>s.SortOrder)
.Load();
}
// TODO: replace the null value with a real context DbSet<Form>
IQueryable<Form> forms = null;
var form = forms.Select(x => new Form()
{
Id = x.Id,
Sections = x.Sections.OrderBy(s => s.SortOrder).Select(s => new Section()
{
Id = s.Id,
SortOrder = s.SortOrder,
Questions = s.Questions.OrderBy(q => q.SortOrder).Select(q => new Question()
{
Id = q.Id,
SortOrder = q.SortOrder
}).ToList()
}).ToList()
}).FirstOrDefault();