C# LINQ到SQL查询父级并计算层次结构中的子级

C# LINQ到SQL查询父级并计算层次结构中的子级,c#,vb.net,linq,linq-to-sql,C#,Vb.net,Linq,Linq To Sql,我接受C#和VB.NET的建议 我正在使用LINQ查询数据。我正在尝试查询父标记并计算子标记 以下是我的标签表列: TagId (int primary) TagName ParentId (int Allow NULL referred to TagId column) 以下是一些示例数据: TagId, TagName, ParentId 1, Web Design, NULL 2, HTML, 1 3, Programming, NULL 4, CSS 3, 1 TagId, Tag

我接受C#和VB.NET的建议

我正在使用LINQ查询数据。我正在尝试查询父标记并计算子标记

以下是我的标签表列:

TagId (int primary)
TagName
ParentId (int Allow NULL referred to TagId column)
以下是一些示例数据:

TagId, TagName, ParentId

1, Web Design, NULL
2, HTML, 1
3, Programming, NULL
4, CSS 3, 1
TagId, TagName, ParentId

1, Web Design, NULL
2, HTML, 1
3, Programming, NULL
4, CSS 3, 1
5, HTML 4, 2
6, HTML 5, 2
问题1:在我的查询结果中,我想用子标记的总和查询所有父标记。如下所示:

Web Design (2 sub tags)
Programming (0 sub tags)
问题2:如果一个子标签也有它自己的子标签

以下是一些示例数据:

TagId, TagName, ParentId

1, Web Design, NULL
2, HTML, 1
3, Programming, NULL
4, CSS 3, 1
TagId, TagName, ParentId

1, Web Design, NULL
2, HTML, 1
3, Programming, NULL
4, CSS 3, 1
5, HTML 4, 2
6, HTML 5, 2
所需的查询结果:

Web Design (4 sub tags)
Programming (0 sub tags)

第二个问题是可选的,但如果你也给出一些建议,那就太好了。谢谢。

好的,最简单的方法是创建一个简单的结构,在其中连接标记(如果标记有父标记),然后遍历子标记以生成每个标记的计数

class Tag
{
    public Tag(int id, int? parentId, string tag)
    {
        Id = id;
        ParentId = parentId;
        TagName = tag;
    }

    public int Id { get; set; }
    public int? ParentId { get; set; }
    public string TagName { get; set; }
}

class TagNode
{
    public Tag Node { get; set; }
    public IList<TagNode> ChildNodes { get; set; }
    public int ChildNodeCount()
    {
        int count = 0;
        if (ChildNodes != null)
        {
            foreach (var node in ChildNodes)
            {
                count += node.ChildNodeCount();
            }
            count += ChildNodes.Count;
        }
        return count;
    }
}

class Program
{
    static void Main(string[] args)
    {
        var tags = new List<Tag>();
        tags.Add(new Tag(1, null, "Web design"));
        tags.Add(new Tag(2, null, "Programming"));
        tags.Add(new Tag(3, 1, "HTML"));
        tags.Add(new Tag(4, 1, "CSS 3"));
        tags.Add(new Tag(5, 3, "HTML 4"));
        tags.Add(new Tag(6, 3, "HTML 5"));

        IList<TagNode> nodes = tags.Select(y => new TagNode { Node = y, ChildNodes = new List<TagNode>() }).ToList();
        foreach (var node in nodes)
        {
            if (node.Node.ParentId.HasValue)
                ConnectNodeToParent(nodes, node);
        }

        // print all nodes
        Console.WriteLine("=== ALL NODES ===");
        nodes.ToList().ForEach(PrintNode);

        // print root nodes
        Console.WriteLine(Environment.NewLine + "=== ROOT NODES ===");
        nodes.Where(y => y.Node.ParentId.HasValue == false).ToList().ForEach(PrintNode);

        Console.ReadLine();
    }

    private static void PrintNode(TagNode node)
    {
        Console.WriteLine("Tag id: {0}, Tag name: {1}, Tag count: {2}", node.Node.Id, node.Node.TagName, node.ChildNodeCount());
    }

    private static void ConnectNodeToParent(IList<TagNode> nodes, TagNode node)
    {
        var parentNode = nodes.Where(y => y.Node.Id == node.Node.ParentId.Value).Single();
        parentNode.ChildNodes.Add(node);
    }
}
class标签
{
公共标记(int-id、int-parentId、string标记)
{
Id=Id;
ParentId=ParentId;
标记名=标记;
}
公共int Id{get;set;}
public int?ParentId{get;set;}
公共字符串标记名{get;set;}
}
类标记节点
{
公共标记节点{get;set;}
公共IList子节点{get;set;}
public int ChildNodeCount()
{
整数计数=0;
if(ChildNodes!=null)
{
foreach(ChildNodes中的var节点)
{
count+=node.ChildNodeCount();
}
count+=ChildNodes.count;
}
返回计数;
}
}
班级计划
{
静态void Main(字符串[]参数)
{
var tags=新列表();
添加(新标签(1,空,“网页设计”);
添加(新标签(2,空,“编程”);
添加(新标签(3,1,“HTML”);
添加(新标签(4,1,“CSS 3”);
添加(新标签(5,3,“HTML4”);
添加(新标签(6,3,“HTML5”);
IList nodes=tags.Select(y=>newtagnode{Node=y,ChildNodes=newlist()}).ToList();
foreach(节点中的var节点)
{
if(node.node.ParentId.HasValue)
ConnectNodeToParent(节点,节点);
}
//打印所有节点
Console.WriteLine(“==所有节点==”);
nodes.ToList().ForEach(PrintNode);
//打印根节点
Console.WriteLine(Environment.NewLine+“==根节点===”;
nodes.Where(y=>y.Node.ParentId.HasValue==false).ToList().ForEach(PrintNode);
Console.ReadLine();
}
私有静态void打印节点(TagNode节点)
{
WriteLine(“标记id:{0},标记名称:{1},标记计数:{2}”,node.node.id,node.node.TagName,node.ChildNodeCount());
}
私有静态void ConnectNodeToParent(IList节点、TagNode节点)
{
var parentNode=nodes.Where(y=>y.Node.Id==Node.Node.ParentId.Value).Single();
parentNode.ChildNodes.Add(节点);
}
}
通过上面的代码,您可以获得每个标记的信息,而不仅仅是“父”标记。

定义:

class Tag
{
    public int Id { get; set; }
    public string TagName { get; set; }

    public int? ParentId { get; set; }      

    public IEnumerable<Tag> Children { get; set; }
}
i、 e

代码:

输出:

Parent (2 sub tags)
NoChildren (0 sub tags)

对于复杂的层次结构,要递归地获取所有子节点,可以使用下一个扩展方法:

public static IEnumerable<Tag> GetChildTags(this Tag tag)
{
    var children = tag.Children ?? Enumerable.Empty<Tag>();
    return children.SelectMany(c => GetChildTags(c)).Concat(children);
}
公共静态IEnumerable GetChildTags(此标记)
{
var children=tag.children??Enumerable.Empty();
返回children.SelectMany(c=>GetChildTags(c)).Concat(children);
}

您找到问题的解决方案了吗?你试过我的解决方案了吗?还没有,我还在四处寻找。Stefan Cruysberghs@有一篇关于LINQ AsHierarchy()扩展方法的文章,无论如何,我也会尝试你的建议。你的建议有VB.NET版本吗?无论如何,我会尝试将其转换为VB。我用非常好的解决方案更新了我的答案。就标签的属性具有公共setter而言,构造函数的存在毫无意义:您可以使用
对象初始值设定项。或者删除setter。你在第一条评论中关注的是错误的东西,这个对象只是为了演示。第二条注释很好。您也可以使用
SelectMany()
Count()
代替
ChildNodeCount()
方法中的foreach循环,或者对于SQL查询,使用CTE:
 var q = from tag in tags
         where tag.ParentId == null
         select new
         {
             Name = tag.TagName,
             ChildrenCount = tag.Children.Count()
         };

foreach (var entry in q)
{
    Console.WriteLine("{0} ({1} sub tags)", entry.Name, entry.ChildrenCount);
}
Parent (2 sub tags)
NoChildren (0 sub tags)
public static IEnumerable<Tag> GetChildTags(this Tag tag)
{
    var children = tag.Children ?? Enumerable.Empty<Tag>();
    return children.SelectMany(c => GetChildTags(c)).Concat(children);
}