Простота отображения таблиц «многие ко многим» в Entity Framework - PullRequest
0 голосов
/ 13 февраля 2020

Я пытаюсь изучить Entity Framework, чтобы попытаться перейти от Linq к SQL, и моя попытка преобразовать некоторый код не удалась для рекурсивной таблицы «многие ко многим» (древовидная структура). Мне нужно выполнить полное чтение таблицы и подготовить дерево в памяти, потому что повторение через базу данных с большим количеством запросов происходит слишком медленно.

У меня есть база данных с таблицей Projects и другая таблица с именем ProjectsTree. С Linq до SQL я могу получить доступ к таблице ProjectsTree, но не с Entity Framework. Он связывает его так, что я не могу напрямую запросить эту таблицу.

Вот код, прежде чем я попытался преобразовать Linq в SQL в Entity Framework, и это сработало. Может быть, я должен придерживаться Linq до SQL и не узнавать что-то новое, и если нет никакого способа сделать это, я могу go назад или позволить двум сосуществовать.

 Private Class cProjectTree2
      Public Project As PDMVault.Project
      Public ChildTree As List(Of cProjectTree2)

   End Class

   ''' <summary>
   ''' Gets the complete PDM project tree that can be added to a tree node.
   ''' Each node has the description in the node text field and the primary key in the tag.
   ''' </summary>
   ''' <returns></returns>
   Public Function GetPDMProjectTree() As TreeNode
      ' To generate the tree, first read the projects table with a complete table scan, then the ProjectTree with a complete table scan.
      ' Create a dictionary of objects of type cRecursiveProjectTree, but only the project is set on the first pass, with a reference to it based on its key.
      ' After the dictionary is built, then link up children to parent using the dictinary.
      ' Finally, use the created tree to create the node structure for the tree view.

      Dim Diag_Stopwatch As New Stopwatch

      Dim IDtoTree As New Generic.Dictionary(Of Long, cProjectTree2)

      Dim C = New PDMVault.DataContext1
      '  C.Log = Console.Out

      Dim Projects = C.Projects.ToList  ' Database list of trees.

''''''''''''''''''''''This is the line that fails.  ProjectTrees only shows up as an association, and I can't seem to get access to it for a full table scan 
          Dim Tree = C.ProjectTrees.ToList   ' Database hierarcy of the projects in the previous statement

'''''''''''''''''''''''''''''''''''''''''''''

      ' Create the dictionary with only the "Project" item set
      For Each P In Projects
         Dim ProjectTreeEntry As New cProjectTree2
         ProjectTreeEntry.ChildTree = New List(Of cProjectTree2)
         ProjectTreeEntry.Project = P
         IDtoTree.Add(P.ProjectID, ProjectTreeEntry)
      Next

      ' Now that the dictionary has been created, the children can be linked to the parent.
      For Each T In Tree
         Dim Parent As cProjectTree2 = Nothing
         Dim Child As cProjectTree2 = Nothing
         IDtoTree.TryGetValue(T.ProjectID, Parent)
         IDtoTree.TryGetValue(T.ChildProject, Child)
         Parent.ChildTree.Add(Child)
      Next

      ' The tree has been built, create the tree view from the tree (using a little recursion)


      Dim GetChildTrees As Func(Of cProjectTree2, TreeNode) =
         Function(P As cProjectTree2) As TreeNode
            Dim Result As New TreeNode
            For Each Child In P.ChildTree.OrderBy(Function(ProjectNode) ProjectNode.Project.Name)
               Dim N = GetChildTrees(Child)
               If N IsNot Nothing Then
                  Result.Nodes.Add(N)
               End If
            Next
            Result.Tag = P.Project.ProjectID
            Result.Text = P.Project.Name
            Return Result
         End Function

      Dim RootProject As cProjectTree2 = Nothing
      If IDtoTree.TryGetValue(1, RootProject) = True Then
         Dim N2 As TreeNode = GetChildTrees(RootProject)
         Return N2
      Else
         Return Nothing
      End If
   End Function

1 Ответ

0 голосов
/ 16 февраля 2020

Я был очень близок к тому, чтобы вернуться назад и придерживаться LINQ к SQL, но это новый проект, и я хотел изучить EF, прежде чем у меня были значительные инвестиции в код. Немного поэкспериментировав с Entities Framework, следующее прекрасно обрабатывает рекурсивное дерево, и мне не нужно получать доступ к таблице ProjectTrees.

 Public Function GetPDMProjectTree() As TreeNode
      Dim Diag_Stopwatch As New Stopwatch
      Diag_Stopwatch.Start()

      Dim C = New PDMVault.DataContext1
      C.Configuration.LazyLoadingEnabled = False ' Necessary for the materialization to work in the next line 
      Dim MaterializeDatabase = C.Projects.ToList

      C.Database.Log = Sub(Log) Debug.Print(Log) ' Verify that only two table scans occurs and that it's not hitting repeatedly

      Dim RootProject = (From P In C.Projects Where P.ProjectID = 1).SingleOrDefault

      If RootProject Is Nothing Then Return Nothing

      Dim GetTree As Func(Of PDMVault.Project, TreeNode) =
         Function(P As PDMVault.Project) As TreeNode
            Dim Result As New TreeNode
            For Each Child In P.Projects1.OrderBy(Function(ProjectNode) ProjectNode.Name)
               Result.Nodes.Add(GetTree(Child))
            Next
            Result.Tag = P.ProjectID
            Result.Text = P.Name
            Return Result
         End Function

      If RootProject Is Nothing Then Return Nothing
      Debug.Print($"Tree building time={Diag_Stopwatch.ElapsedMilliseconds / 1000:#0.00}")
      Return GetTree(RootProject)

   End Function

Для архивов SO вот первый метод, который делал это с двумя поездки в базу данных (одна для начального словаря и одна для root) и использовала внешний словарь, прежде чем я узнал об отключении отложенной загрузки, вероятно, не так оптимально, как мое окончательное решение.

 Public Function GetPDMProjectTree2() As TreeNode

      Dim Diag_Stopwatch As New Stopwatch

      Dim C = New PDMVault.DataContext1

      C.Database.Log = Sub(Log) Debug.Print(Log) ' Verify that only one table scan occurs and that it isn't an N+1 problem.

      ' Force a complete table scan before starting the recursion below, which will come from cached content

      Dim ProjectTreeFromDatabase = (From P In C.Projects
                                     Select Project = P,
                                            Children = P.Projects1).ToDictionary(Function(F) F.Project.ProjectID)


      Dim GetTree As Func(Of PDMVault.Project, TreeNode) =
         Function(P As PDMVault.Project) As TreeNode
            Dim Result As New TreeNode
            For Each Child In ProjectTreeFromDatabase(P.ProjectID).Children.OrderBy(Function(ProjectNode) ProjectNode.Name)
               Dim N = GetTree(Child)
               If N IsNot Nothing Then
                  Result.Nodes.Add(N)
               End If
            Next
            Result.Tag = P.ProjectID
            Result.Text = P.Name
            Return Result
         End Function

      Dim RootProject = (From P In C.Projects Where P.ProjectID = 1).SingleOrDefault
      If RootProject Is Nothing Then Return Nothing
      Return GetTree(RootProject)

   End Function

Оба решения предотвращают повторные поездки в базу данных.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...