Как я могу создать функцию, используя LINQ, чтобы получить указанный узел? - PullRequest
1 голос
/ 17 августа 2011

Я использую этот код из другой сети:

Как мне моделировать этот класс в базе данных?

В каждой объективной записи есть поле с именем «Ранг». Это говорит мне, что позиция. Например:

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

Ответы [ 2 ]

1 голос
/ 17 августа 2011

Вот способ 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, },
};

Далее я создалпоиск, чтобы получить детей от любого идентификатора.

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.

Iтеперь определил 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();

Так что теперь у меня есть функция, которая будет возвращать ноль или более целей для любого предоставленного ключа уровня.Приятно то, что это выполнит только один запрос к базе данных, и вызовы функции map будут возвращены в o (1) раз.

Это работает для вас?

1 голос
/ 17 августа 2011

Редактировать:

Возможно, вам лучше передать экземпляр 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.

...