如何LINQ查询代码首先生成EF6分层实体(实体内的实体)?

时间:2014-03-09 00:55:04

标签: vb.net linq linq-to-entities

[images&从处理XAML&的另一个线程中获取的代码树视图数据here]

专家,

摘要

我使用EF& amp;创建一个2个或更多分层对象Code First,数据库创建所有外键和&奇妙地填补他们。但是因为我无法在代码中看到这些键,所以我无法找到一种方法来创建返回父级,子级,该子级等的LINQ查询。


简易结构

Access_Group - > Access_List - > Object_Group - > ...... (但示例/校样只需要2个级别)

首先,这是用于创建最高'父级'项目的 top 级别类:

Public Class Access_Group
    Inherits EntityTypeConfiguration(Of Access_Group)
    Implements IAccess_Group

    <Key>
    Public Property Access_GroupID As Integer Implements IAccess_Group.Access_GroupID
    Public Property Name As String Implements IAccess_Group.Name
    Public Property LastUpdated As Date Implements IAccess_Group.LastUpdated
    Public Property Active As Boolean Implements IAccess_Group.Active
    '------------------------------------------------------------------
    Public Property Access_Lists As ObservableCollection(Of Access_List) Implements IAccess_Group.Access_Lists
    Public Sub New()
        Me.Access_Lists = New ObservableCollection(Of Access_List)
    End Sub
End Class

正如您所看到的,我使用ObservableCollection创建一个子/子...当我这样做并且 Add-Migration Foo 时,VS2012中的EF6会创建您期望的内容:

enter image description here

......并且在资源管理器中:

enter image description here

...在典型的,可接受的EF样式中,外键列在MSSQL2012中自动创建为:

enter image description here

大!

现在上面我没有手动创建关系,就是......我这样做了:

Private Sub AddData()
    Try
        ctx = New entitiesContext
        Dim d As Date = Now
        '--------------------------------
        Dim al As New Access_List
        ' lower classes not needed to be shown...
        With al
            .Active = True
            .Checked = True
            .LastUpdated = d
            .Name = "some access-list at " & d.ToLongTimeString
        End With
        '--------------------------------
        Dim ag As Access_Group = New Access_Group
        With ag
            .Access_Lists.Add(al)
            .Active = True
            .LastUpdated = d
            .Name = "some access-group at " & d.ToLongTimeString
        End With
        '
        ctx.Access_Groups.Add(ag)
        '
        Dim i As Integer = ctx.SaveChanges()
        Console.WriteLine("Seed complete! -> " & i)
    Catch ex As Exception
        Dim exText As String = "Seed Failed "
        Console.WriteLine(exText & "(Message): " & ex.Message)
        Console.WriteLine(exText & "(ToString): " & ex.ToString)
        Console.WriteLine(exText & "(StackTrace): " & ex.StackTrace)
        Console.WriteLine("EntityValidationErrors: ")
        For Each eve As System.Data.Entity.Validation.DbEntityValidationResult In ctx.GetValidationErrors()
            Console.WriteLine("eve: OK? " & eve.IsValid & " - " & eve.Entry.ToString)
            For Each devr As System.Data.Entity.Validation.DbValidationError In eve.ValidationErrors
                Console.WriteLine("devr invalid property: " & devr.PropertyName)
                Console.WriteLine("devr error message : " & devr.ErrorMessage)
            Next
        Next
    End Try
End Sub

所以你看,我用(顶级):

ctx.Access_Groups.Add(AG)

...添加Access_Group类,但只有在将与之关联/相关的Access_List类添加为:

之后
With ag
    .Access_Lists.Add(al)

...当然,我们的Access_List类拥有它自己的子节点,因为之前添加了Object_Groups:

With al
    .Object_Groups.Add(og)

......等等。基本上,每个子ID都添加到父级,最后通过我们的DBContext添加顶级父级。


问题/证据

完成这样的LINQ查询后:

Dim ags = From ag In ctx.Access_Groups Select ag

我完全期望不仅仅是顶级课程,但如果我挖到每个课程,我也会得到他们的孩子,孙子等等。

事实并非如此!

简单的证明/调试Sub liek如下:

Public Sub PrintDebug(TheList As IEnumerable(Of Access_Group))
    For Each ag As Access_Group In TheList
        Console.WriteLine("=======================================")
        Console.WriteLine("ag: " & ag.Name & " has " & ag.Access_Lists.Count & " Access_List entries")
        For Each al As Access_List In ag.Access_Lists
            Console.WriteLine("ag -> al: " & al.Name & " has " & al.Object_Groups.Count & " Object_Group entries")
            For Each og As Object_Group In al.Object_Groups
                Console.WriteLine("ag -> al -> og: " & og.Name & " has " & og.Network_Objects.Count & " Network_Object entries")
                '...
            Next
        Next
        Console.WriteLine("=======================================")
    Next
End Sub

...让我认为(愚蠢/无知)成为一个惊喜:

=======================================
ag: some access-group at 5:00:49 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:08:56 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:09:14 PM has 0 Access_List entries
=======================================
=======================================
ag: some access-group at 5:12:31 PM has 0 Access_List entries
=======================================
[...]

WTF?

在POCO中,您可以获得整个班级 - 包括该实例的所有孩子。但是在EF(我正在这样做的方式)中,耦合是松散的。


需要

不知何故,我需要为数据库中的那些顶级项目执行LINQ查询,并且自动让数据库中与之关联的所有子项都与之相关。

但是因为代码/业务逻辑不能看到FK,所以我不能简单地测试密钥之间的相等性......

如何比我更聪明的人对这样的等级关系进行LINQ查询&amp;得到所有的好东西?


不想要

  • 求助于笨重的DataSet和DataTables(去过那里,做到了, 也可以在那时抛弃EF)......
  • 直接SQL查询(否定EF和Linq效率,如延迟加载......
  • 不知何故不得不手动破解MS SQL表以将FK行数据复制到 '可见'列(yuk)......

最优雅的方式通常是最好的...

你有什么想法?

TIA!

1 个答案:

答案 0 :(得分:0)

专家,

通过坚持不懈,坚持不懈和盲目运气,我想我发现了这个问题 - 或者至少是一个原因......

简短版本:我必须查找原因,但在CF EF6中,我无意中未启用“模型中的外键列”,如:

enter image description here

但是 ...我使用 Code First ,而不是数据库优先 ...我只是包含上面的图片所以你能理解我所说的方面。 :)

鉴于上面的PrintDebug()子,以下查询:

Dim agList As List(Of Access_Group) = _
    (From ag In ctx.Access_Groups.Include("Access_Lists") Select ag).ToList
Me.AccessGroups = New ObservableCollection(Of Access_Group)(agList)
Me.PrintDebug(Me.AccessGroups)

...产生以下结果(与上面的/ OP相比):

[...]
=======================================
ag: some access-group at 5:00:49 PM has 1 Access_List entries
ag -> al: some access-list at 5:00:49 PM has 0 Object_Group entries
=======================================
=======================================
ag: some access-group at 5:08:56 PM has 1 Access_List entries
ag -> al: some access-list at 5:08:56 PM has 0 Object_Group entries
=======================================
=======================================
ag: some access-group at 5:09:14 PM has 1 Access_List entries
ag -> al: some access-list at 5:09:14 PM has 0 Object_Group entries
=======================================
=======================================
ag: some access-group at 5:12:31 PM has 1 Access_List entries
ag -> al: some access-list at 5:12:31 PM has 0 Object_Group entries
=======================================
[...]

因此查询中的操作差异是:

ctx.Access_Groups.**Include**("Access_Lists") Select ag

......现在在Lerman&amp; amp;当我创建类时,Miller将在Code First中找到我/我应该如何做到这一点(或者如果我很幸运,如何添加 - 迁移/更新 - 数据库并且 in situ ! )

仍然欢迎任何输入!

感谢您的考虑!

P.S。堆栈丢失了我的原始帐户,我不得不重新开始(多年后),所以现在我甚至无法编辑,评论或更新我的帖子或评论......呃......我会尝试用CF更新将FK作为列启用的方法... 如果我可以 :(