问题是我不明白如何使用LINQ获取所有类别和子类别并将它们添加到列表中,以便我可以在视图中显示它们。
public class Category
{
public int ID { get; set; }
public string Title { get; set; }
public int? ParentID { get; set; }
public virtual Category Parent { get; set; }
}
目前我只有这种方法来获取所有类别。
public async Task<IEnumerable<Category>> GetAllCategories()
{
return await _context.Categories.ToListAsync();
}
这就是我想要实现的目标:
答案 0 :(得分:2)
所以,如果我从这个数据开始:
var categories = new List<Category>()
{
new Category() { ID = 0, ParentID = null, Title = "First Link" },
new Category() { ID = 1, ParentID = null, Title = "Second Link" },
new Category() { ID = 2, ParentID = null, Title = "Third Link" },
new Category() { ID = 3, ParentID = null, Title = "Fourth Link" },
new Category() { ID = 4, ParentID = null, Title = "Fifth Link" },
new Category() { ID = 5, ParentID = 0, Title = "First Child Link" },
new Category() { ID = 6, ParentID = 0, Title = "Second Child Link" },
new Category() { ID = 7, ParentID = 0, Title = "Third Child Link" },
new Category() { ID = 8, ParentID = 6, Title = "First Grandchild Link" },
new Category() { ID = 9, ParentID = 6, Title = "Second Grandchild Link" },
new Category() { ID = 10, ParentID = 6, Title = "Third Grandchild Link" },
};
...然后我可以这样做:
var lookup = categories.ToLookup(x => x.ParentID);
这会创建一个查找,可用于查找任何父ID的所有子项。在您的情况下,您应该能够_context.Categories.ToLookup(x => x.ParentID);
。您可能需要在.ToArray()
之前点击.ToLookup
。
好消息是,这只会打击数据库一次。
现在递归遍历数据变得容易了。这有三种方法:
(1)
Func<ILookup<int?, Category>, int?, int, IEnumerable<string>> formatTree = null;
formatTree = (l, p, i) =>
from c in l[p]
from t in new[] { "".PadLeft(i * 4) + c.Title }.Concat(formatTree(l, c.ID, i + 1))
select t;
(2)
public IEnumerable<string> FormatTree(ILookup<int?, Category> lookup, int? parent, int indent)
{
return
from c in lookup[parent]
from t in new[] { "".PadLeft(indent * 4) + c.Title }.Concat(FormatTree(lookup, c.ID, indent + 1))
select t;
}
(3)
public IEnumerable<string> FormatTree2(ILookup<int?, Category> lookup, int? parent, int indent)
{
foreach (var category in lookup[parent])
{
yield return "".PadLeft(indent * 4) + category.Title;
foreach (var descendant in FormatTree2(lookup, category.ID, indent + 1))
{
yield return descendant;
}
}
}
所有三个人用不同的语法以相同的方式做同样的事情。
我得到了这个输出:
First Link First Child Link Second Child Link First Grandchild Link Second Grandchild Link Third Grandchild Link Third Child Link Second Link Third Link Fourth Link Fifth Link
目前尚不清楚您的确切输出是什么 - 我假设您不想创建PNG图像 - 但您应该能够使用它来获得所需的内容。
答案 1 :(得分:0)
分层树结构并不适合运行LINQ查询,因为(遗憾的是)需要扩展您想要包含的每个层次结构的查询。
此查询应该将所有顶级类别作为具有直接子项的组:
var query = from category in _context.Categories
where category.ParentID == null
from subCategory in _context.Categories
where subCategory == category.ID
group subCategory by category;
如果你只有两个级别的层次结构,这应该可以正常工作,但要真正获得任何类型顺序的树结构,你需要展平层次结构,这通常使用递归函数完成,每次调用它(单独)展平树的一个节点。
对于这种结构,标准SQL查询或LINQ查询并没有真正削减它。但是,实体框架(以及可能还有其他ORM解决方案)将为您完成所有工作。
您可以在模型中创建一些正确归因的导航属性,如下所示:
public virtual IEnumerable<Category> SubCategories { get; set; }
public virtual Category ParentCategory { get; set; }
但是如果您选择不在模型中包含导航属性,则需要以艰难的方式执行此操作,每次调用递归函数时都会向数据层发出请求。
public void RenderSubCategoriesAsHtml(int? parentID = null)
{
var children =
from category in _context.Categories
where category.ParentID == parentID
select category;
if (children.Any())
{
Response.WriteLine("<ul>");
foreach (var child in children)
{
Response.WriteLine("<li><a href=\"#addProperUrl\">");
Response.WriteLine(Server.EncodeHtml(child.Title));
Response.WriteLine("</a>");
RenderSubCategoriesAsHtml(child.ID);
Response.WriteLine("</li>");
}
Response.WriteLine("</ul>");
}
}
只需将对Response.WriteLine
的调用更改为更适合您应用程序架构的内容。
答案 2 :(得分:0)
EF有办法做到这一点。但您必须在Category
和Subcategory
var all categoriesWithSubCategories = _context.Categories.Include("Subcategory").ToList();
现在,当您从categoriesWithSubCategories
列表中选择一个类别时,您将可以访问属于所选类别的所有子类别。
添加更多细节以进一步解释;
正如我之前提到的,你必须在同一个表上建立FK关系才能做到这一点。
这意味着您的ParentId
字段与您的Id
字段有外键关系,这是您的PK字段。
现在稍微更改您的实体类以表示上述关系。
public partial class Category
{
public Category()
{
this.SubCategory = new HashSet<Category>();
}
public int Id { get; set; }
public string Title { get; set; }
public Nullable<int> ParentId { get; set; }
public virtual ICollection<Category> SubCategory { get; set; }
public virtual Category ParentCategory { get; set; }
}
一旦完成,您只需要一行Linq(上图)就可以为您的类别和子类别构建完美的树层次结构。