LINQ: пройти вверх по иерархии, чтобы получить родительскую иерархию

У меня есть некоторые данные, которые представляют отношения родитель-потомок в одном и том же объекте. Учитывая узел, мне нужно найти всю его верхнюю иерархию (родительский, прародительский, прапрародительский и т. д.).

Моя сущность такая:

 public partial class Location{
        public int LocationId { get; set; }
        public int? FkParentLocationId { get; set; }
        ..... more properties here.......
        public virtual Location FkParentLocation { get; set; }
        public virtual ICollection<Location> InverseFkParentLocation { get; set; }
}

Я имею в виду реализацию обхода иерархии, предложенную здесь, но она работает, когда вы идете вниз по иерархии. Как получить верхнюю иерархию с помощью LINQ?

Пример данных:

            List<Location> locations = new List<Location> {
            new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
            new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
            new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
            new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
            new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
            new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
            new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" }            };

Ожидаемый результат: учитывая идентификатор местоположения: 5, я должен получить список, содержащий местоположения 3 и 2 (поскольку они являются родителями).


person devC    schedule 17.12.2019    source источник
comment
не могли бы вы предоставить образцы данных?   -  person StepUp    schedule 17.12.2019
comment
Можете ли вы предоставить более подробную информацию о том, чего вы пытаетесь достичь? пример с примерными данными которые вы разместили, ввод-вывод   -  person Cata Hotea    schedule 17.12.2019
comment
Добавил запрошенные детали в вопрос.   -  person devC    schedule 17.12.2019


Ответы (1)


Вот подход, который вы могли бы использовать, продемонстрированный с помощью консольного приложения. Много заимствований у Джона Скита.

using System;
using System.Collections.Generic;
using System.Linq;

namespace Locations
{
    public partial class Location 
    {
        public int LocationId { get; set; }
        public int? FkParentLocationId { get; set; }
        public virtual Location FkParentLocation { get; set; }
        public virtual ICollection<Location> InverseFkParentLocation { get; set; }
        public string LocationName { get; set; }
        public string LocationDisplayName { get; set; }      
    }  

    class Program
    {
        static void Main(string[] args)
        {
            List<Location> locations = new List<Location> {
            new Location { LocationId = 5, FkParentLocationId = 3, LocationName = "Windsor", LocationDisplayName = "Windsor"},
            new Location { LocationId = 15, FkParentLocationId = 3, LocationName = "Hampshire", LocationDisplayName = "Hampshire" },
            new Location { LocationId = 12, FkParentLocationId = 3, LocationName = "Sussex", LocationDisplayName = "Sussex"},
            new Location { LocationId = 13, FkParentLocationId = 3, LocationName = "Willowood", LocationDisplayName = "Willowood"},
            new Location { LocationId = 1, FkParentLocationId = 3, LocationName = "Gerbshire", LocationDisplayName = "Gerbshire"},
            new Location { LocationId = 3, FkParentLocationId = 2, LocationName = "Lincoln", LocationDisplayName = "Lincoln"},
            new Location { LocationId = 2, LocationName = "Mains", LocationDisplayName = "Mains" }            };


            var result  = GetAncestorsIds(locations, 5);
            foreach (var id in result)
            {
                System.Console.WriteLine(id);
            }          
        }

        private static IEnumerable<int> GetAncestorsIds(List<Location> locations, int id)
        {
            Location location = locations.SingleOrDefault(l => l.LocationId == id);
            if(location != null)
            {   
                while(location != null && location.FkParentLocationId != null)
                {
                    location = locations.SingleOrDefault(l => l.LocationId == location.FkParentLocationId);
                    if(location != null)
                    {
                        yield return location.LocationId;
                    }
                }
            }   
        }
    }
}

И этот подход можно превратить в собственное расширение Linq. Вот как это могло выглядеть.

public static class MyExtensions
{
    public static IEnumerable<int> GetAncestorIds<TSource>(this IEnumerable<TSource> source, Func<TSource, int> pk, Func<TSource, int?> fk, int id)
    {
        TSource currentObj = source.SingleOrDefault(s => pk(s) == id);

        while(currentObj != null && fk(currentObj) != null)
        {
            currentObj = source.SingleOrDefault(s => pk(s) == fk(currentObj));
            if(currentObj != null)
            {
                yield return pk(currentObj);
            }
        }
    }
}

а затем, чтобы вызвать это для своего сценария, вы бы сделали это

var result = locations.GetAncestorIds(l => l.LocationId, l => l.FkParentLocationId, 5);
person Dave Barnett    schedule 24.12.2019