如何使用 LINQ 创建函数来获取指定节点?
我正在使用来自另一个网站的代码:
我在每个目标记录中都有一个名为“Rank”的字段。它告诉我什么是职位。例如:
Objective "Geometry": Rank1
|_Objective "Squares": Rank1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
|_Objective "Types": Rank1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3
该排名告诉我节点的顺序。但我想获得所有排名:
Objective "Geometry": Rank1
|_Objective "Squares": Rank1 -> 1.1
|_Objective "Circles": Rank2
|_Objective "Triangle": Rank3
|_Objective "Types": Rank1 -> 1.3.1
Objective "Algebra": Rank2
Objective "Trigonometry": Rank3 -> 3
我正在使用 LINQ to SQL。
<TreeView Name="treeView1">
<TreeView.ItemTemplate>
<HierarchicalDataTemplate DataType="{x:Type data:Objective}" ItemsSource="{Binding Path=Objectives}" >
<TextBlock Text="{Binding Name}" />
</HierarchicalDataTemplate>
</TreeView.ItemTemplate>
</TreeView>
我需要一个 linq 函数来获取指定的节点。我的意思是,一个通过级别 (1.2)、(1.3.1) 获取节点的函数,
如果存在,则返回节点,如果不为空。
更新1:
这并不是一个真正的函数,但我意识到最好创建一个 getNode 函数。
private void AddButton_Click(object sender, RoutedEventArgs e)
{
NorthwindDataContext cd = new NorthwindDataContext();
int[] levels = LevelTextBox.Text.ToIntArray('.');
string newGroupName = NameTextBox.Text;
Objective currentObjective = null;
int? identity = null;
for (int i = 0; i < levels.Length - 1; i++ )
{
int currentRank = levels[i];
if (identity == null)
{
currentObjective = (from p in cd.Objective
where p.Level == currentRank && p.Parent_ObjectiveID == null
select p).SingleOrDefault();
}
else
{
currentObjective = (from p in cd.Objective
where p.Level == currentRank && p.Parent_ObjectiveID == identity
select p).SingleOrDefault();
}
if (currentObjective == null)
{
MessageBox.Show("Levels don't exist");
return;
}
else
{
identity = currentObjective.ObjectiveID;
}
}
if (currentObjective != null)
{
if (levels.Last() == currentObjective.Level)
{
MessageBox.Show("Level already exists");
return;
}
}
else
{
var aux = (from p in cd.Objective
where p.Parent_ObjectiveID == null && p.Level == levels.Last()
select p).SingleOrDefault();
if (aux != null)
{
MessageBox.Show("Level already exists");
return;
}
}
var newObjective = new Objective();
newObjective.Name = NameTextBox.Text;
newObjective.Level = levels.Last();
newObjective.Parent_ObjectiveID = currentObjective == null ? null : (int?)currentObjective.ObjectiveID ;
cd.Objective.InsertOnSubmit(newObjective);
cd.SubmitChanges();
}
更新2:
public Objective GetNode(params int[] indexes)
{
return GetNode(null, 0, indexes);
}
public Objective GetNode(int? parentid, int level, params int[] indexes)
{
NorthwindDataContext cd = new NorthwindDataContext();
Objective item = null;
if (indexes.Length == 0)
return null;
if (parentid == null)
{
item = (from p in cd.Objective
where p.Level == indexes[level] && p.Parent_ObjectiveID == null
select p).SingleOrDefault();
}
else
{
item = (from p in cd.Objective
where p.Level == indexes[level] && p.Parent_ObjectiveID == parentid
select p).SingleOrDefault();
}
if (item == null)
return null;
if (++level < indexes.Length)
item = GetNode(item.ObjectiveID, level, indexes);
return item;
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
编辑:
您可能最好传入 NorthwindDataContext 实例,而不是每次传递都创建一个新实例。
您可以通过创建如下方法来做到这一点,该方法已经过重构,因此不需要递归,这应该对可读性有所帮助。
调用方式如下:
GetNode(cd.Objective, LevelTextBox.Text.ToIntArray());
原始:
您可以使用类似的方法,它只是一个简单的扩展方法:
并且将由
treeView1.Get(1,3,1);
使用,或者在您编辑的情况下,treeView1.Get(1,3,1);
使用。 Get(LevelTextBox.Text.Split('.').Select(x => int.Parse(x)).ToArray()); 但是,这有零错误处理对于无效输入。如果您不能确定所有项目都是 TreeViewItem 对象,您可以将
tree.Items[...]
替换为tree.ItemContainerGenerator.ContainerFromIndex(...)< /code> (与
i.Items
相同)但是,这些更改将要求 TreeView 已完全呈现。
Edit:
You're probably best to pass in an instance of the NorthwindDataContext versus creating a new one with each pass.
You could do this by creating a method as below, which has been refactored so that it doesn't need to be recursive which should help a little in the readability department.
To be called like:
GetNode(cd.Objective, LevelTextBox.Text.ToIntArray());
Original:
You could use something like this, it's just a simple Extension method:
And would be used by
treeView1.Get(1,3,1);
or in the case of your edit,treeView1.Get(LevelTextBox.Text.Split('.').Select(x => int.Parse(x)).ToArray());
however, this has zero error handling for invalid input.If you can't be certain that all the items will be TreeViewItem objects, you can replace the
tree.Items[...]
withtree.ItemContainerGenerator.ContainerFromIndex(...)
(and same withi.Items
These changes will require the TreeView to have been rendered fully, however.
这是执行此操作的 LINQ 方法。
我假设了
Objective
的定义如下:然后我创建了一个名为
LevelObjective
的支持类来捕获级别(即“1.3.1”),如下所示:我已经开始定义了一系列目标:
接下来,我创建了一个查找来从任何 id 中获取子项。
我使用此查找来创建一组顶级目标:
然后定义了一个使层次结构扁平化的函数:
我已经将其中一个定义为使用泛型的扩展方法,但我只是将其重构为 lambda 表达式使用了LevelObjective。
我现在定义了获取任何
LevelObjective
的子级所需的Func>
。然后,我可以根据原始的
Objective
对象集创建一个完整的LevelObjective
对象列表。最后我可以把它变成一张从关卡到目标的地图。
现在,要找到任何目标,我只需调用此代码:
现在我有一个函数,将为任何提供的级别键返回零个或多个目标。好处是,这只会对数据库执行一次查询,并且对
map
函数的调用会在 o(1) 时间内返回。这对你有用吗?
Here's a LINQ way of doing this.
I've assumed a definition of
Objective
like so:I've then created a support class called
LevelObjective
to capture the level (ie "1.3.1") like so:And I've begun with a collection of objectives defined so:
Next I created a look up to get the children from any id.
I used this look up to create a set of the top-level objectives:
I then defined a function that flattens a hierarchy:
I already had one of these defined as an extension method that used generics, but I just refactored into a lambda expression that used
LevelObjective
.I now defined the
Func<LevelObjective, IEnumerable<LevelObjective>>
required to get the children of anyLevelObjective
.I could then create a complete list of
LevelObjective
objects based on the original set ofObjective
objects.Finally I can turn this into a map from level to objectives.
Now, to find any objective I just have to call this code:
So now I have a function that will return zero or more objectives for any supplied level key. The nice thing is that this will perform only one query on the database and calls to the
map
function are returned in o(1) time.Does this work for you?