如何使用LINQ创建一个函数来获取指定的节点?

时间:2011-08-17 00:50:13

标签: c# wpf linq linq-to-sql linq-to-objects

我正在使用另一个网站上的代码:

How can I model this class in a database?

我在每个客观记录中都有一个名为“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)

获取节点

如果存在,则返回节点,如果不为null。

更新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;
    }

2 个答案:

答案 0 :(得分:1)

修改:

您可能最好传入NorthwindDataContext的实例,而不是每次传递都创建一个新实例。

你可以通过创建一个如下方法来做到这一点,该方法已被重构,因此它不需要递归,这应该对可读性部门有所帮助。

    public Objective GetNode(IEnumerable<Objective> collection, params int[] indices)
    {
        Objective current = null;

        for (int t = 0; t < indices.Length; t++)
        {
            Objective item = collection.SingleOrDefault(x => x.Parent == current && x.Rank == indices[t] - 1);

            if (item == null)
                return null;
        }

        return current;
    }

被称为:GetNode(cd.Objective, LevelTextBox.Text.ToIntArray());


原件: 你可以使用这样的东西,它只是一个简单的扩展方法:

    public static TreeViewItem Get(this TreeView tree, params int[] indexes)
    {
        if (tree == null)
            return null;

        if (indexes == null || indexes.Length == 0)
            return null;

        TreeViewItem i = tree.Items[indexes[0] - 1] as TreeViewItem;

        for (int index = 1; index < indexes.Length; index++)
        {
            i = i.Items.Count >= indexes[index] - 1 ? i.Items[indexes[index] - 1] as TreeViewItem : null;

            if (i == null)
                return null;
        }

        return i;
    }

并且会被treeView1.Get(1,3,1);使用,或者在您的编辑时使用treeView1.Get(LevelTextBox.Text.Split('.').Select(x => int.Parse(x)).ToArray());但是,对于无效输入,这会有错误处理。

如果您无法确定所有项目都是TreeViewItem对象,则可以将tree.Items[...]替换为tree.ItemContainerGenerator.ContainerFromIndex(...)(与i.Items相同

但是,这些更改将要求TreeView完全呈现。

答案 1 :(得分:1)

这是一种LINQ方式。

我假设Objective的定义如此:

public class Objective
{
    public int ObjectiveId { get; set; }
    public int? Parent_ObjectiveId { get; set; }
    public string Name { get; set; }
    public int Rank { get; set; }
}

然后我创建了一个名为LevelObjective的支持类来捕获关卡(即“1.3.1”),如下所示:

public class LevelObjective
{
    public Objective Objective { get; set; }
    public string Level { get; set; }
}

我开始时已定义了一系列目标:

var objectives = new []
{
    new Objective { ObjectiveId = 1, Parent_ObjectiveId = null, Name = "Geometry", Rank = 1, },
    new Objective { ObjectiveId = 2, Parent_ObjectiveId = 1, Name = "Squares", Rank = 1, },
    new Objective { ObjectiveId = 3, Parent_ObjectiveId = 1, Name = "Circles", Rank = 2, },
    new Objective { ObjectiveId = 4, Parent_ObjectiveId = 1, Name = "Triangle", Rank = 3, },
    new Objective { ObjectiveId = 5, Parent_ObjectiveId = 4, Name = "Types", Rank = 1, },
    new Objective { ObjectiveId = 6, Parent_ObjectiveId = null, Name = "Algebra", Rank = 2, },
    new Objective { ObjectiveId = 7, Parent_ObjectiveId = null, Name = "Trigonometry", Rank = 3, },
};

接下来,我创建了一个查找以获取任何ID的孩子。

var lookup = objectives.ToLookup(x => x.Parent_ObjectiveId);

我用这个查找来创建一组顶级目标:

var roots = lookup[null]
    .Select(o => new LevelObjective()
    {
        Objective = o,
        Level = o.Rank.ToString(),
    });

然后我定义了一个使层次结构变平的函数:

Func<
    IEnumerable<LevelObjective>,
    Func<LevelObjective, IEnumerable<LevelObjective>>,
    IEnumerable<LevelObjective>> flatten = null;

flatten = (rs, f) =>
    rs.Concat(
        from r in rs
        from c in flatten(f(r), f)
        select c);

我已经将其中一个定义为使用泛型的扩展方法,但我只是重构为使用LevelObjective的lambda表达式。

我现在定义了获取Func<LevelObjective, IEnumerable<LevelObjective>>的孩子所需的LevelObjective

Func<LevelObjective, IEnumerable<LevelObjective>> getChildren = lo =>
    from o in lookup[lo.Objective.ObjectiveId]
    select new LevelObjective()
    {
        Objective = o,
        Level = String.Format("{0}.{1}", lo.Level, o.Rank),
    };

然后,我可以根据原始的LevelObjective个对象创建Objective个对象的完整列表。

var levelObjectives = flatten(roots, getChildren);

最后,我可以将其转换为从关卡到目标的地图。

var map = levelObjectives.ToLookup(x => x.Level, x => x.Objective);

现在,要找到任何目标,我只需要调用此代码:

var objective = map["1.3.1"].FirstOrDefault();

所以现在我有一个函数将为任何提供的级别键返回零个或多个目标。好消息是,这只会对数据库执行一次查询,并且会在o(1)时间内返回对map函数的调用。

这对你有用吗?