linq 递归链接以获得无限子项

qeeaahzv  于 2022-12-06  发布在  其他
关注(0)|答案(3)|浏览(138)

我是linq的新手,我正在尝试找到一种方法来返回一个给定父节点的父节点和一个List(子节点)以及所有子节点。我有一个Locations表,其中包含字段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无法辨识方法,而且这个方法无法转译为存放区运算式。

nnsrf1az

nnsrf1az1#

你能试试这个吗?

public IEnumerable<Location> GetChild(int id)
        {
            DBEntities db = new DBEntities();
        var locations = db.Locations.Where(x => x.ParentLocationID == id || x.LocationID == id).ToList();

    var child = locations.AsEnumerable().Union(
                                db.Locations.AsEnumerable().Where(x => x.ParentLocationID == id).SelectMany(y => GetChild(y.LocationId))).ToList();
            return child;
        }
62o28rlo

62o28rlo2#

首先,您可能希望将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);
    }
e5nszbig

e5nszbig3#

因此,当我自己试图弄清楚这一点并失败时,我做了一些研究。我看到的大多数文章都说,这在Linq的单个查询中是不可能的。
你可以做的一件事就是收集所有的对象ID,然后只从中获取你需要的对象,然后用这些对象在内存中构建一个树。获取ID应该很快,特别是索引。
例如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

相关问题