Linq查询递归

时间:2011-12-28 20:53:44

标签: linq recursion

我在C#和Entity框架中工作。 我的数据库中有一个名为Genre的表。这是它的属性: idGenre,name,idParentGenre。

例如。价值观将是:

(idGenre = 1,name =“acoustic”,idParentGenre = 2)

(idGenre = 2,name =“rock”,idParentGenre = 2)

(idGenre = 3,name =“country”,idParentGenre = 4)

(idGenre = 4,name =“folk”,idParentGenre = 5)

(idGenre = 5,name =“someOtherGenre”,idParentGenre = 5)

正如你所看到的,它是一种树。

现在,我有一个搜索这个表的方法。输入参数是idGenre和idParentGenre。如果类型(idGenre)是idParentGenre的儿子/孙子/ grandgrandchild / ...我应该返回。

例如,我得到idGenre = 3,idParentGenre = 5,我应该返回true。

然而,Linq没有递归。有没有办法可以做到这一点?

3 个答案:

答案 0 :(得分:4)

我会创建一个方法来处理它,而不是使用LINQ:

bool HasParent(int genre, int parent)
{
    Genre item = db.Genres.FirstOrDefault(g => g.IdGenre == genre);
    if (item == null)
        return false;

    // If there is no parent, return false, 
    // this is assuming it's defined as int?
    if (!item.idParentGenre.HasValue)
        return false;

    if (item.idParentGenre.Value == parent)
        return true;

    return HasParent(item.idParentGenre, parent);
}

这使您可以在单个递归函数中处理此问题。

答案 1 :(得分:2)

看起来你正试图在不使用树的情况下实现树。

你考虑过......用树吗?这是一个great question,你可以建立一些答案(包括一个code的答案:

delegate void TreeVisitor<T>(T nodeData);

class NTree<T>
{
    T data;
    LinkedList<NTree<T>> children;

    public NTree(T data)
    {
        this.data = data;
        children = new LinkedList<NTree<T>>();
    }

    public void addChild(T data)
    {
        children.AddFirst(new NTree<T>(data));
    }

    public NTree<T> getChild(int i)
    {
        foreach (NTree<T> n in children)
            if (--i == 0) return n;
        return null;
    }

    public void traverse(NTree<T> node, TreeVisitor<T> visitor)
    {
        visitor(node.data);
        foreach (NTree<T> kid in node.children)
            traverse(kid, visitor);
    }        
}

答案 2 :(得分:1)

将类型表放在内存中(它不能那么大),并递归遍历它以在idGenre和其后代的传递闭包之间创建映射,如下所示:

1: {1, 2}
2: {2}
3: {3, 4, 5}
4: {4, 5}
5: {5}

上述数据仅在内存中 。每次启动时以及对类型表的更新都会重新计算它。

当需要查询特定类型的所有歌曲时,请在idGenre in ...查询中使用预先计算的表格,如下所示:

IEnumerable<Song> SongsWithGenreId(int idGenre) {
    var idClosure = idToIdClosure[idGenre];
    return context.Songs.Where(song => idClosure.Contains(song.idGenre));
}