2017-06-02 47 views
0

我是新来的linq,我试图找到一种方式来返回父母和一个List(子女)和所有这些孩子,对于一个给定的父母。我有一个地点表的字段为LocationID,ParentLocationID,LocationName。对这些数据的样本可能看起来像这样:递归linq得到无限的孩子​​

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

鉴于数据,如果选择父“ABC”,我想回到所有行,因为所有的孩子都在它之下。但是,如果我选择父ABC3,它将返回DEF4,GHI1,GHI2,DEF5。

我看着这些所谓的问题,但我仍然困惑于如何创造这个声明(S):

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实体无法识别的方法和这种方法不能被翻译成表达商店。

+0

你会得到什么错误? –

+0

我刚刚更新了问题,对不起,应该先把它放在那里。 – Robert

+0

我的建议是加载父节点,然后调用递归CTE加载其他记录,并让EF做它的魔术实体修复 - 或者相反,调用递归CTE,它将加载所有记录,然后获得父母(以及所有的孩子都应该在场)。 –

回答

1

你可以试试这个..

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

看起来像我得到相同的错误消息:LINQ to Entities不识别方法'System.Collections.Generic.IEnumerable'1 [位置] GetChild(Int32)'方法,并且此方法不能转换为存储表达式。 – Robert

+0

同样的错误....虽然我使用GetChild(id),因为y没有名为id的属性。您是不是只想把名为'id'的参数放在那里呢? – Robert

+0

是的,确实是id试试 – ISHIDA

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); 
    } 
+0

我希望我有SQL,但我不......这是一个新的设计,我们想用EF代替存储过程(我知道你可以在EF中使用它们)。 – Robert

+0

如果我把Linq语句放到LinqPad中,它会返回这个SQL,这对我来说似乎很奇怪:DECLARE @ p0 Int = 2 DECLARE @ p1 Int = 2 - EndRegion SELECT [t0]。[LocationID], [t0]。[LocationName],[t0]。[ParentLocationID],[t0]。[PrimaryAddressID],[t0]。[OfficePhone],[t0]。[AltPhone],[t0]。[InstallDate],[t0 ] [LocationTypeID] FROM [CoreSchema]。[Location] AS [t0] WHERE([t0]。[ParentLocationID] = @ p0)OR([t0]。[LocationID] = @ p1) 我说因为它只是做一个'OR'而不是其他的。 – Robert

+0

@Robert请检查我的最新编辑。 –

0

所以,虽然试图弄清楚这一点,我没有做一些研究。我遇到的大多数文章都表示,在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