我想从一个具有以下定义的表中获取下面的Hierarchical/Tree数据.
Tree Table:
"""""""""""
Id |ParentId
"""""""""""
Work1|null
Work2|Work1
Work3|Work2
...
Run Code Online (Sandbox Code Playgroud)
必需的查询结果数据(不需要选项卡) - 如果我选择'Work1',我应该完成其根目录下的ID.如果我选择'Work2',那么我也应该在它的根之上和之下完成ID.
> Work1
----------
> Work2
----------
> Work3
---------
Run Code Online (Sandbox Code Playgroud)
NHibernate以优化的方式在上述场景中获取数据的最佳方式是什么.
要找出"最佳方式"是什么,需要有关实际情况的更多信息.您在寻找什么样的"优化"?最少量的数据(只有你真正需要的行)或最少数量的SQL查询(最好是一次往返数据库)或其他任何数据?
场景1:一次加载并在内存中保留较长时间的菜单或树结构(不是每隔几秒更新一次的列表).表中的行数很少(小是相对的,但我会说200以下的任何行).
在这种情况下,我只需要使用以下一个查询获取整个表:
var items = session.Query<Work>()
.Fetch(c => c.ParentWork)
.Fetch(c => c.ChildWorks).ToList();
var item = session.Get<Work>(id);
Run Code Online (Sandbox Code Playgroud)
这将导致单个SQL查询,它只是从表中加载所有行.item将包含完整的树(父母,祖父母,孩子等).
场景2:大量行,只需要一小部分行.预计层次结构中只有少数级别.
在这种情况下,只需加载项目,并通过延迟加载让NHibernate到其余部分,或强制它通过编写递归方法来遍历父项和子项来加载所有内容.这将导致N + 1选择,这可能会或可能不会比情景1慢(取决于您的数据).
这是一个快速的黑客证明:
var item = session.Get<Work>(id);
Work parent = item.ParentWork;
Work root = item;
// find the root item
while (parent != null)
{
root = parent;
parent = parent.ParentWork;
}
// scan the whole tree
this.ScanChildren(root);
// -----
private void ScanChildren(Work item)
{
if (item == null)
{
return;
}
foreach (Work child in item.ChildWorks)
{
string name = child.Name;
this.ScanChildren(child);
}
}
Run Code Online (Sandbox Code Playgroud)
编辑:
场景3:大量数据.查询次数最少,数据量最小.
在这种情况下,我认为不是树结构,而是我们一个接一个地加载数据层.
var work = repo.Session.Get<Work>(id);
// get root of that Work
Work parent = work.ParentWork;
Work root = work;
while (parent != null)
{
root = parent;
parent = parent.ParentWork;
}
// Get all the Works for each level
IList<Work> worksAll = new List<Work>() { root };
IList<Work> worksPerLevel = new List<Work>() { root };
// get each level until we don't have any more Works in the next level
int count = worksPerLevel.Count;
while (count > 0)
{
worksPerLevel = this.GetChildren(session, worksPerLevel);
// add the Works to our list of all Works
worksPerLevel.ForEach(c => worksAll.Add(c));
count = worksPerLevel.Count;
}
// here you can get the names of the Works or whatever
foreach (Work c in worksAll)
{
string s = c.Name;
}
// this methods gets the Works in the next level and returns them
private IList<Work> GetChildren(ISession session, IList<Work> worksPerLevel)
{
IList<Work> result = new List<Work>();
// get the IDs for the works in this level
IList<int> ids = worksPerLevel.Select(c => c.Id).ToList();
// use a WHERE IN clause do get the Works
// with the ParentId of Works in the current level
result = session.QueryOver<Work>()
.Where(
NHibernate.Criterion.Restrictions.InG<int>(
NHibernate.Criterion.Projections.Property<Work>(
c => c.ParentWork.Id),
ids)
)
.Fetch(c => c.ChildWorks).Eager // this will prevent the N+1 problem
.List();
return result;
}
Run Code Online (Sandbox Code Playgroud)
这个解决方案不会导致N + 1问题,因为我们对子节点使用了一个急切的负载,因此NHibernate将知道子列表的状态而不是再次命中DB.您将只获得x + y选择,其中x是查找根的选择数,Worky是级别数(树的最大深度).
| 归档时间: |
|
| 查看次数: |
3974 次 |
| 最近记录: |