【问题标题】:How can I get "levels" of a self referencing table in LINQ?如何在 LINQ 中获取自引用表的“级别”?
【发布时间】:2011-07-19 22:19:12
【问题描述】:

有一个表 Category 带有一个 pk idCategory 和一个自引用外键 fiCategory。这意味着当fiCategory is null 时,类别是“主要类别”。如果fiCategory 链接到另一个类别,则它是它的子类别。但是这个子类别也有 1-n 个子类别(fiCategory 链接到它的idCategory)也是有效的。

问:如何获得主类、子类、子类、子类等的列表。使用 LINQ?

背景

我正在使用类型化数据集来比较来自 Server1/MySQL 的数据与来自 Server2/MS SQL-Server 的数据。规范化和清理后(有几个不一致的地方)我想将新数据导入 SQL-Server。首先我必须导入主要类别,然后是子类别等等。否则,当我尝试将具有外键的行插入尚未插入的类别时,SQL-Server 将引发约束异常。

这些是表(左侧 MySQL 源表,右侧 SQL-Server 目标表):

这里我得到了 MySQL 中不在 SQL-Server 中的新行:

srcdest 是类型化数据集

Dim idSrc = From c In src.kategorie Select c.kategorie_id
Dim idDest = From c In dest.Category Select c.idCategory
Dim diff = idSrc.Except(idDest)
Dim needUpdate = diff.Any

现在我想导入新行。 这样我就得到了所有的“主要类别”:

Dim mainCat = From kat In src.kategorie
            Join d In diff
            On kat.kategorie_id Equals d
            Where kat.IsparentNull
        Select kat

For Each cat In mainCat
    Dim newCat = Me.dest.Category.NewCategoryRow
    newCat.idCategory = cat.kategorie_id
    newCat.Name = cat.name
    newCat.SetfiCategoryNull()
    dest.Category.AddCategoryRow(newCat)
    rowsUpdated += daCategoryOut.Update(dest.Category)
Next

这样我得到了所有的子类别:

Dim subCat = From kat In src.kategorie
               Join d In diff
               On kat.kategorie_id Equals d
               Where Not kat.IsparentNull
          Select kat

两个 LINQ 查询都在工作,但我如何获得子类别的所有“级别”?我需要将行从“顶部”插入到“底部”。有没有一种方法可以在任何深度下都有效?

至少这不起作用(重复 pk 值):

Dim subCatWithChild = From cat In subCat
                      Join child In 
                     (From kat In src.kategorie Where Not kat.IsparentNull)
                      On child.parent Equals cat.kategorie_id
                   Select cat

我仍在学习 LINQ 并欣赏任何类型的建议(也在 C# 中)。提前谢谢你。

注意:也许您知道一种方法,我可以暂时禁用 SQL-Server 中的外键约束,并在我从 ADO.NET 插入所有行后启用它。那会简单得多。


这是解决方案,感谢@Tridus:

Dim mainCat = From kat In src.kategorie
          Where kat.IsparentNull
      Select kat

For Each kat In mainCat
   rowsUpdated += insertCategory(kat, diff, daCategoryOut)
Next

这是递归函数:

Private Function insertCategory(ByVal parent As CC_IN.kategorieRow, ByVal diff As IEnumerable(Of Int32), ByVal daCategoryOut As CC_OutTableAdapters.CategoryTableAdapter) As Int32
    Dim rowsInserted As Int32 = 0

    If diff.Contains(parent.kategorie_id) Then
        Dim newCat = Me.dest.Category.NewCategoryRow
        newCat.idCategory = parent.kategorie_id
        newCat.Name = parent.name
        If parent.IsparentNull Then
            newCat.fiCategory = parent.parent
        Else
            newCat.SetfiCategoryNull()
        End If
        dest.Category.AddCategoryRow(newCat)
        rowsInserted += daCategoryOut.Update(dest.Category)
    End If

    'get all childs from this parent
    Dim childs = From cat In Me.src.kategorie
               Where Not cat.IsparentNull AndAlso cat.parent = parent.kategorie_id
               Select cat
    'insert all childs for this parent
    For Each child In childs
        rowsInserted += insertCategory(child, diff, daCategoryOut)
    Next

    Return rowsInserted
End Function

【问题讨论】:

  • 解决方案应放入自己的答案中并标记为已接受的答案,或者您应编辑现有已接受的答案并将“解决方案”添加到其中,以便解决方案存在于一个位置。
  • 我接受了 Tridus 的回答,并将我的实际解决方案添加到我的问题中清晰可见,因此我不明白我会做得更好。

标签: .net vb.net linq ado.net linq-to-dataset


【解决方案1】:

Yes, Foreign Key constraints can be temporarily disabled.

执行此操作的最佳方法(除了禁用外键并仅逐行复制整个表)是从主要类别开始递归。从概念上讲,你会这样做:

Get the main categories (which you've done)
For each main category
   Is this one in the other DB? If not, add it.
   Get the sub-categories of this main category.
   For each sub-category
     Is this one in the other DB? If not, add it.
     Get the sub-categories of this sub-category.

等等。很容易获得您当前拥有的任何类别的子类别,因此如果您从顶部开始,您可以遍历整个树并在另一侧添加任何缺少的内容。

【讨论】:

  • 谢谢。这意味着我应该递归调用一个函数,该函数采用父类别并只要有孩子就插入所有孩子?但是出于兴趣,是否有一个 LINQ 解决方案可以像 T-SQL 中的递归 CTE 一样为我提供所有级别?
  • 我认为没有,没有。您将设置一个存储过程并调用它以便在服务器上执行递归操作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多