如何在LINQ中获取自引用表的“级别”?

时间:2011-07-19 22:19:13

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

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

问:如何获取主要类别,子类别,“子子类别”等列表。用LINQ?

Backgound

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

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

Kategorie: MySQL-source Category: SQL-Server destination

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

srcdest是类型化的DataSet

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

1 个答案:

答案 0 :(得分:2)

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.

等。很容易得到你目前所拥有的任何类别的子类别,所以如果你只是从顶部开始,你可以走遍整个树并添加另一边缺少的任何东西。