我有以下自引用表
public partial class products_category
{
public long id { get; set; }
public string category_name { get; set; }
public string category_description { get; set; }
//self referencing to table id
public Nullable<long> Parent_Id { get; set; }
public string navPath {get; set; }
}
此处字符串navpath包含子类别的所有主要父项,例如:
"Clothes" = 1 Parent_id=null, navpath=""
"Silk" = 2 Parent_id=1 navpath="1"
"Silk Suit"=3 parent_id=2 navpath="1-2"
"Saree" =4 parent_id=3 navpath="1-2-3"
"Dress Material"=5 parent_id=1 navpath="1" and so on....
现在,根据这种情况,我想访问展平树以进行某一深度的进一步处理,只能说到2级或直到与navpath相关联的子级别4级。
我对这个问题的看法是以这种方式使用linq:
var catTrees = db.products_category.Where(pc => pc.navpath.Split('-').Length < 4).ToList();
我使用以下链接进行进一步的遍历和树生成: https://bitlush.com/blog/recursive-hierarchical-joins-in-c-sharp-and-linq
到目前为止,它正在做一项伟大的工作,唯一的问题是我不想预先选择整个表进行处理。我希望在第一次迭代时实现分页和一定程度的深度,因此我可以在数千条记录的情况下保持性能。 [将此视为类别层次结构或博客/ youtube评论层次结构]。但是使用上面的ef linq命令会出现以下错误:
The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities.
我在ef文档和SO中的其他地方检查过,知道string.split无法隐式使用EF。但是我们可以使用扩展方法应用它吗?或者这个树选择是否可以使用替代方法而不使用string.split并仅使用DB? 请建议。
答案 0 :(得分:1)
这看起来像是在LINQ mpre中构建SQL代码的问题,特别是SQL,它将字符串拆分为破折号并对元素进行计数。
如果你不讨厌加载到内存然后你可以强迫任何东西:)
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Split('-').Length < 4).ToList();
这里的技巧是当我们想要数据库中的数据时,通过添加.ToList()
来强制执行SQL。这称为实现数据。
即使有了这种实现技巧,计数也会更快
var catTrees = db.products_category.ToList().Where(pc => pc.navpath.Count(a => a == '-') < 3).ToList();
这些解决方案基本上与
相同List<Result> filter() {
List<Result> r = new List<Result>();
foreach(var a in db.products_category) {
if(a.navpath.Count(a => a == '-') < 3) {
r.add(a);
}
}
return r;
}
在考虑它时,过滤器方法的内存密集程度要低一些,因为它只读取一个并且永远不会将所有内容存储在内存中。 (理论上至少,只有少数人真正知道.NET编译器在阴影中做了什么)
答案 1 :(得分:0)
我建议你不要使用navpath
来检查深度。
如果您可以更改模型,则可以为每个类别添加一个额外的数字Depth
字段,并根据其navpath
填充它,然后您可以通过以下方式从代码中选择它们:
var catTrees = db.products_category.Where(pc => pc.Depth < 3).ToList();
有很多方法可以填充新列,但最重要的是你必须只执行一次(假设你每次修改类别的navpath
时都会跟踪它)。
填充它的一种可能方法是循环遍历所有类别,例如:
var allCategories = db.products_category.ToList();
foreach(var category in allCategories)
{
var depth = category.navpath == "" ? 0 : category.navpath.Split('-').Length + 1;
category.Depth = depth;
}
allCategories.SubmitChanges();