递归linq以获得无限子女

时间:2017-06-02 20:47:17

标签: c# asp.net-mvc entity-framework linq linq-to-entities

我是linq的新手,我正在尝试找到一种方法来返回父级和一个List(子级)以及所有这些子级,用于给定的父级。我有一个地点表,其中包含字段LocationIDParentLocationIDLocationName。数据样本可能如下所示:

ABC
 --ABC1
 --ABC2
 ----DEF1
 ----DEF2
 ----DEF3
 --ABC3
 ----DEF4
 ------GHI1
 ------GHI2
 ----DEF5
 --ABC4
 ...

鉴于该数据,如果所选父项是'ABC',我想返回所有行,因为所有子项都在其下。但是,如果我选择了父'ABC3',它将返回DEF4,GHI1,GHI2,DEF5。

我已经研究了这些SO问题,但我仍然对如何创建这个语句感到困惑:

Find all descendants in self-referencing (parent-child) hierarchical tree

LINQ to SQL - Self Join Child to Parent (Same Table)

这是我尝试过的但是收到错误:

public ActionResult Index()
{
    var loc = GetChild(346);
    return View(loc);
}

public IEnumerable<Location> GetChild(int id)
{
    DBEntities db = new DBEntities();

    var locations =  db.Locations.Where(x => x.ParentLocationID == id || x.LocationID == id).Union(
                        db.Locations.Where(x => x.ParentLocationID == id).SelectMany(y => GetChild(y.LocationID)));
    return locations;
}

Location类是:

public class Location
{
    public Location();

    public virtual ICollection<SimSystem> SimSystems { get; set; }
    public virtual ICollection<LocationAddress> LocationAddresses { get; set; }
    public virtual LocationType LocationType { get; set; }
    public virtual ICollection<CustomerUser> CustomerUsers { get; set; }
    public virtual ICollection<AppUserLocation> AppUserLocations { get; set; }
    public int LocationTypeID { get; set; }
    public DateTime? InstallDate { get; set; }
    public string AltPhone { get; set; }
    public string OfficePhone { get; set; }
    public int? PrimaryAddressID { get; set; }
    public int? ParentLocationID { get; set; }
    public string LocationName { get; set; }
    public string LocationName2 { get; set; }
    public int LocationID { get; set; }
    public virtual Address Address { get; set; }
}

错误是:

LINQ to Entities无法识别该方法,并且此方法无法转换为商店表达式。

3 个答案:

答案 0 :(得分:1)

你可以试试吗..

{{1}}

答案 1 :(得分:0)

首先,您需要让DBEntities退出递归方法。您最终会在数据库上建立太多连接,最终会导致内存泄漏 至于例外。它声明对GetChild(int);的调用无法转换为linq到sql表达式。如果您有要生成的sql,我可以帮助您将其翻译为linq。

编辑:
所以我能够重新创建你的代码。这不是最佳的,因为它将使用sql查询多次调用数据库。

public class Recursive
{
    BlogContext db = new BlogContext();
    public int Counter { get; set; } = 0;
    public Recursive()
    {
        db.Database.Log += (str) => //this will log all the calls to the database.
        {
            System.Diagnostics.Debug.WriteLine(str, "Sql Query: ");
        };
    }

    public List<Location> StartRecursive()
    {
        return GetChild(50).ToList();
    }
    public IEnumerable<Location> GetChild(int id)
    {
        var locations = db.Locations
        .Where(x => x.ParentLocationID == id || x.LocationID == id).ToList();
        if (locations.Count == 1) return locations;
        var locationSubset = locations.Where(tt=>tt.LocationID!=id)
        .SelectMany(tt => GetChild(tt.LocationID)).ToList();
        Counter++;
        return locations.Union(locationSubset);
    } 

答案 2 :(得分:0)

因此,在尝试自己弄清楚并且失败时,我做了一些研究。我遇到的大多数文章都表示,在使用Linq的单个查询中,这是不可能的。

您可以做的一件事是收集层次结构中对象的所有ID。然后只抓取你需要的对象,然后用它在内存中用对象构建一个树。抓取ID应该非常快,特别是对于索引。

e.g。 List<int> locationIDs = db.Locations.Where(x => x.ParentLocationID == id || x.LocationID == id).Select(x => x.LocationID).ToList();

然后你可以

var locations = db.Locations.Where(x => locationIDs.Contains(x.LocationID);

然后使用普通的递归方法构建树。 不过我还想提一个我发现的扩展方法,据说可以为你做这个。使用信息图表等描述非常详细。 http://www.scip.be/index.php?Page=ArticlesNET23