【问题标题】:Linq query recursionLinq 查询递归
【发布时间】:2012-01-29 11:28:12
【问题描述】:

我在 C# 和实体框架中工作。 我的数据库中有一个名为 Genre 的表。以下是它的属性: idGenre、姓名、idParentGenre。

例如。值将是:

(idGenre = 1, name = "声学", idParentGenre=2)

(idGenre = 2, name = "rock", idParentGenre=2)

(idGenre = 3, name = "国家", idParentGenre=4)

(idGenre = 4, name = "folk", idParentGenre=5)

(idGenre = 5, name = "someOtherGenre", idParentGenre=5)

如您所见,它有点像一棵树。

现在,我有一种方法可以在此表中进行搜索。输入参数是 idGenre 和 idParentGenre。如果流派 (idGenre) 是 idParentGenre 的儿子/孙子/孙子/...,我应该返回。

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

但是,Linq 中没有递归。有什么办法可以做到吗?

【问题讨论】:

  • “Linq 中的递归”是什么意思。你能展示你拥有的方法吗?它有什么问题?
  • 用老式的方式来做。 LINQ 很棒,但它不能解决所有问题。

标签: linq recursion


【解决方案1】:

我会制定一种方法来处理这个问题,而不是使用 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);
}

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

【讨论】:

    【解决方案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);
        }        
    }
    

    【讨论】:

    • 我不能使用树。为此,我的数据库中有一个表。
    【解决方案3】:

    将流派表带入内存(不可能那么大),并递归遍历它以创建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));
    }
    

    【讨论】:

    • 但这不是一个完美的解决方案。我已经有一个放置父级ID的地方(这个地方是桌子),我不认为另一个用于相同目的的地方是好的。
    • @Srcee 它不是存储数据的“另一个地方”,它是相同数据的缓存,只是经过预处理。您的查询会非常快 - 比迄今为止建议的任何其他内容都要快得多,因为只有一次往返。所有递归都将被纳入idToIdClosure 字典的结构中,这很好,因为流派不会经常变化。
    猜你喜欢
    • 2017-08-13
    • 1970-01-01
    • 1970-01-01
    • 2014-01-25
    • 1970-01-01
    • 2012-02-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多