【问题标题】:using string.split in entity to traverse tree depth在实体中使用 string.split 遍历树深度
【发布时间】:2017-02-11 20:14:30
【问题描述】:

我有以下自引用表

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....

现在根据这种情况,我想访问 flattend 树以对特定深度进行进一步处理,仅说与 navpath 关联的子级的第 2 级或第 4 级深度。

我对这个问题的想法是通过这种方式使用 linq to ef:

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 cmets 层次结构]。

但是使用上面的 ef linq 命令会出现以下错误:

The LINQ expression node type 'ArrayLength' is not supported in LINQ to Entities.

我检查了 ef 文档和 SO 中的其他地方,知道 string.split 不能隐式地与 EF 一起使用。但是我们可以使用扩展方法应用它,或者这个树选择是否可以有替代方法而不使用 string.split 并点击 DB only? 请指教。

【问题讨论】:

    标签: c# sql entity-framework linq tree


    【解决方案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 编译器在暗中做了什么)

    【讨论】:

    • 检查您的建议
    • 不走运 DbExpressionBinding 需要一个带有集合 ResultType 的输入表达式。
    • 好的,值得一试。但这是一个始终有效的解决方案。
    • 但是如果有 100 条记录,这不会将所有内容都加载到内存中,从而破坏“性能”的目的吗?
    • 它最明确地将所有内容加载到内存中,但这为在其上运行不符合 SQL 的代码提供了可能性。无论如何,几百行并不多。一个字符是 32 位的,这意味着 1 兆字节的数据约为 33000 个字符。
    【解决方案2】:

    我建议您不要使用 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();
    

    【讨论】:

      猜你喜欢
      • 2019-05-13
      • 2019-01-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多