C# ; } }
实际上,我不知道是否可以通过优雅的LINQ查询来实现。这是DFS的递归版本,它通过C# ; } },c#,linq,entity-framework,hierarchy,C#,Linq,Entity Framework,Hierarchy,实际上,我不知道是否可以通过优雅的LINQ查询来实现。这是DFS的递归版本,它通过ParentID public static IEnumerable<TempTable> SortedList(IEnumerable<TempTable> list = null, int? ParentID = null, ILookup<int?, TempTable> lookup = null) { if (lookup == null) lo
ParentID
public static IEnumerable<TempTable> SortedList(IEnumerable<TempTable> list = null, int? ParentID = null, ILookup<int?, TempTable> lookup = null)
{
if (lookup == null)
lookup = list.ToLookup(x => x.ParentID, x => x);
foreach (var p in lookup[ParentID].OrderBy(x => x.SortOrder))
{
yield return p;
foreach (var c in SortedList(lookup: lookup, ParentID: p.ID))
yield return c;
}
}
公共静态IEnumerable-SortedList(IEnumerable-list=null,int?ParentID=null,ILookup-lookup=null)
{
如果(查找==null)
lookup=list.ToLookup(x=>x.ParentID,x=>x);
foreach(查找[ParentID].OrderBy(x=>x.SortOrder)中的var p)
{
收益率p;
foreach(SortedList中的变量c(查找:查找,父ID:p.ID))
收益率c;
}
}
请查看我的编辑,不知道您是否收到了没有此评论的编辑通知。请查看我的编辑,不知道您是否收到了没有此评论的编辑通知。有多少深度?如果可以有无限的深度,那么在单个查询中是不可能的。实体框架的工作方式也是如此,它在递归性质的查询中失败。唯一的解决方案是树遍历。它可以有无限的深度,不应有任何限制。请修改您的代码,以便初始列表(在您的案例上下文中为.tentrables)可以是一个已经查询过的IEnumerable,并按要求传递和排序,因为您可能会注意到其他2个“新”答案(托马斯B.和罗曼·佩卡尔)这两种方案都有解决方案。您将如何做到这一点?谢谢您的时间。@RandRandom我已经添加了答案,它将对所有预加载的内容执行相同的工作。不确定在实体框架环境中是否需要查找,EF是否已经为主键/外键值提供了内置键搜索?如果是,则为n在什么情况下,在“正常”的基础上进行查找更好LINQ查询,或者如果您连续查询一个INT值使用查找,它更像是一个gernal的东西吗?嗯,这是一个更通用的解决方案,使用列表,而不是表,不能说实体框架中的内置键。因此,这里的查找有助于不迭代每个ID的整个列表。您可以解释一下为什么使用非递归版本吗?堆栈不是cau吗唱一首不必要的歌?默认(functionn调用).NET应用程序的堆栈大小为4MB@64-点点滴滴1MB@32-位。此处使用的堆栈数据收集驻留在堆上,并受.NET对象堆大小限制2GB的限制。因此,对于非常深的层次结构,递归版本会在非递归版本之前抛出异常。此外:递归实现通常较慢由于调用开销(尽管如此:非递归更复杂,缺乏可读性)
ID = 1
ParentID = null
Name = Test 1
SortOrder = 1
ID = 2
ParentID = 1
Name = Test 2
SortOrder = 1
ID = 3
ParentID = 1
Name = Test 3
SortOrder = 2
ID = 4
ParentID = 2
Name = Test 4
SortOrder = 1
Test 1 //root element with sort order 1 = very top
Test 2 //child element of root with sort order 1
Test 4 //child element of test 2 with sort order 1
Test 3 //child element of root with sort order 2
from x in EntityModel.TempTables //DbSet<TempTable> by EntityFramework - which already holds all elements
orderby x.SortOrder
from y in x.TempTableChildren //Navigation Property by EntityFramework
orderby y.SortOrder
select y
ID = 193475037
ParentID = 2
Name = Test 192375937
SortOrder = 25
ID = 193475037
ParentID = 456798424
Name = Test 192375937
SortOrder = 4
public class LinqTestDemo
{
Random rand = new Random();
List<TempTable> list = new List<TempTable>();
public List<TempTable> GetFlatData()
{
list = GetTestData();
var rootElement = (from x in list
where x.ParentID == null
orderby x.SortOrder
select x).ToList();
var flatList = OrderChilds(rootElement).ToList();
foreach (var tempTable in flatList)
{
Console.WriteLine(string.Format("ID = {0} - ParentID = {1} - Name = {2} - SortOrder = {3}", tempTable.ID, tempTable.ParentID, tempTable.Name, tempTable.SortOrder));
}
return flatList;
}
private IEnumerable<TempTable> OrderChilds(List<TempTable> enumerable)
{
foreach (var tempTable in enumerable)
{
yield return tempTable;
TempTable table = tempTable;
var childs = OrderChilds((from x in list
where x.ParentID == table.ID
orderby x.SortOrder
select x).ToList());
foreach (var child in childs)
{
yield return child;
}
}
}
public List<TempTable> GetTestData()
{
var returnValue = new List<TempTable>();
for (int i = 0; i < 50; i++)
{
var tempTable = new TempTable();
tempTable.ID = i;
if (i == 0)
tempTable.ParentID = null;
else
tempTable.ParentID = rand.Next(0, i);
var maxSortOrder = (from x in returnValue
where x.ParentID == tempTable.ParentID
select (int?)x.SortOrder).Max();
if (maxSortOrder.HasValue)
tempTable.SortOrder = maxSortOrder.Value + 1;
else
tempTable.SortOrder = 1;
tempTable.Name = string.Format("Test {0:00}", i);
returnValue.Add(tempTable);
}
return returnValue;
}
public class TempTable
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
}
}
public class Item
{
public int ID { get; set; }
public int? ParentID { get; set; }
public string Name { get; set; }
public int SortOrder { get; set; }
}
public void DoWork()
{
Item[] data = new Item[] {
new Item() { ID = 2, ParentID = 1, Name = "Test 2", SortOrder = 1},
new Item() { ID = 3, ParentID = 1, Name = "Test 3", SortOrder = 2},
new Item() { ID = 4, ParentID = 2, Name = "Test 4", SortOrder = 1},
new Item() { ID = 1, ParentID = null, Name = "Test 1", SortOrder = 1},
};
var result = from x in data
orderby x.SortOrder, x.ParentID
select x;
foreach (var row in result.ToArray())
{
Console.WriteLine(row.Name);
}
}
public class TempTable
{
public int ID {get;set;}
public int? ParentID {get;set;}
public String Name {get;set;}
public int SortOrder {get;set;}
}
public List<TempTable> GetTempData()
{
var temp = new List<TempTable>();
temp.Add(new TempTable { ID = 1, ParentID = null, Name = "Test 1", SortOrder = 1 });
temp.Add(new TempTable { ID = 2, ParentID = 1, Name = "Test 2", SortOrder = 1 });
temp.Add(new TempTable { ID = 3, ParentID = 1, Name = "Test 3", SortOrder = 3 });
temp.Add(new TempTable { ID = 4, ParentID = 2, Name = "Test 4", SortOrder = 1 });
temp.Add(new TempTable { ID = 5, ParentID = 1, Name = "Test 5", SortOrder = 2 });
return temp;
}
var data = GetTempData();
var result = data.OrderBy(d => d.SortOrder).ThenBy(d => d.ParentID);
//Do something with result
public lEnumerable<TempTable> GetList( int? parentID = null){
foreach ( var item in Context.TempTables
.Where( x => x.ParentID == parentID )
.OrderBy( x=> x.SortOrder)
.ToList() {
yield return item;
foreach( var child in GetList( item.ID))
{
yield return child;
}
}
}
var sortedList = GetList();
public IEnumerable<TempTable> PrepareList(IEnumerable<TempTable> list){
list = list.OrderBy( x=> x.SortOrder);
foreach(var item in list){
yield return item;
foreach(var child in PrepareList(item.ChildTempTables)){
yield return child;
}
}
}
// since EF will automatically fill each children on fetch
// all we need is just a top level nodes
// which we will pass to PrepareList method
var list = Context.TempTables.ToList().Where(x=> x.ParentID == null);
var sortedList = PrepareList(list).ToList();
// it is good to create list at the end if you are going to
// iterate it many times and logic will not change.
public static IEnumerable<TempTable> PreorderForest(IEnumerable<TempTable> list)
{
var nodesByParent = list.GroupBy(x => x.ParentID.GetValueOrDefault(-1))
.ToDictionary(xs => xs.Key,
xs => xs.OrderBy(x => x.SortOrder).GetEnumerator());
var stack = new Stack<IEnumerator<TempTable>>();
stack.Push(nodesByParent[-1]);
while (stack.Count > 0)
{
var nodes = stack.Peek();
if (nodes.MoveNext())
{
yield return nodes.Current;
IEnumerator<TempTable> children;
if (nodesByParent.TryGetValue(nodes.Current.ID, out children))
stack.Push(children);
}
else
stack.Pop();
}
}
public static IEnumerable<TempTable> SortedList(IEnumerable<TempTable> list = null, int? ParentID = null, ILookup<int?, TempTable> lookup = null)
{
if (lookup == null)
lookup = list.ToLookup(x => x.ParentID, x => x);
foreach (var p in lookup[ParentID].OrderBy(x => x.SortOrder))
{
yield return p;
foreach (var c in SortedList(lookup: lookup, ParentID: p.ID))
yield return c;
}
}