在 Linq 中,如何将类型检查添加到 'include clauses' 不止一层深?
In Linq, how do I add type checking to 'include clauses' more than one level deep?
这是型号:
Public Class Parent
Public Property Name As String
Public Property ID As Integer
Public Property child1 As New List(Of Child1)
End Class
Public Class Child1
Public Property ID As Integer
Public Property Name As String
Public Property Child2 As New List(Of Child2)
End Class
Public Class Child2
Public Property ID As Integer
Public Property Name As String
Public Property Child3 As New List(Of Child3)
End Class
Public Class Child3
Public Property ID As Integer
Public Property Name As String
Public Property Child4 As New List(Of Child4)
End Class
Public Class Child4
Public Property ID As Integer
Public Property Name As String
End Class
要在我一直使用的查询中包含所有 table:
TheDataContext.Parent.Include("Child1").Include("Child1.Child2").Include("Child1.Child2.Child3").Include("Child1.Child2.Child3.Child4").Where(some condition).ToList()
所以这太乱了,而且没有类型检查。如果我只需要深入一个 child table 的话,我确实学习了以下作品:
TheDataContext.Parent.Include(NameOf(Child1)).Where(some condition).ToList()
但是如果我像这样在下一个 child 级别尝试应用 'NameOf' 技术
.Include(NameOf(Child1.Child2))...
在运行时失败,说 Parent 没有声明名称为 Child1.Child2 的导航 属性,回想起来这是有道理的,但我不知道如何解决这个问题。如果我使用
.Include(NameOf(Parent.Child1.Child2))...
编辑说引用 non-shared 成员需要 object 引用失败,我理解,但我不认为这是解决方案,我的模型不应该使用共享。
这个 SO Post 建议在 Include 中使用一个 lambda,Select 包含另一个 lambda,我在 C# 中理解,但在 vb.net 中,.include 似乎没有甚至允许第一个 lambda。以下在编辑器中失败:
.include(function(x) x.Child1) or anything else in the lambda
也许我缺少一个导入,但除此之外我根本不知道在 vb.net 中执行的语法在 SO post.[=19 中看起来很优雅=]
如何将 children 的所有级别转换为类型检查?
在应用以下建议和答案后添加:
不要忽视 Harald Coppoolse 的出色回答,这就是我将 string-based.Include's 转换为带有类型检查的 .Include 的方式。虽然这在技术上是我的问题的答案,但正如 Harald 的回答所示,答案不一定是正确的答案,因为可能有一个重要的问题没有被问到。
TheDataContext.Parent.Include(Function(a) a.child1.Select(Function(b) b.child2.Select(Function(c) c.child3.Select(Function(d) d.child4)))).ToList()
我的答案是在 C# 中,但这个想法也适用于 VB。我想你已经明白要点了。
使用 Select
而不是 Include
。 Select 是完全类型安全的
数据库查询中速度较慢的部分之一是从数据库管理系统到本地计算机的数据传输。将此数据限制为仅您实际计划使用的数据是明智的。
您使用 Include 传输的数据比您使用的多。例如,您的 Child1 有一个 ID。 Child1 的所有 Child2 Children 都有一个等于 Child1 的 Id 的外键 Child1Id。
所以如果你 select Child1 的 Id 为 4,而这个 Child1 有 100 Children,所有这些 Children 将有一个等于 4 的外键 Child1Id。因此你正在转移Child1 的主键值的 101 倍,而您已经知道它们都将具有相同的值。
如果您使用 Select
而不是 Include
,您将能够 select 只有您真正打算使用的属性。 Select
的另一个副作用是它是完全类型安全的,这将解决您的问题。
根据我使用 Entity Framework 的经验,如果我打算更改获取的值,我只会使用 Include
。 DbContext 只能在获取后更改值(否则您将不得不使用变通方法)。
因此,如果您想要所有(或部分)Parents 及其所有 Children、GrandChildren 等,并且您希望其类型安全,请执行以下操作:
var parentsWithTheirDescendants = myDbContext.Parents
.Where(parent => ...)
.Select(parent => new
{
// select only the properties you plan to use
Id = parent.Id,
Name = parent.Name,
Children = parent.Child1s
.Where(child1 => ...) // only if you don't want all Children
.Select(child1 => new
{
// again: select only the properties you plan to use
Id = child1.Id,
Name = child1.Name
// not needed:
// ParentId = child1.ParentId
GrandChildren = child1.Child2s
.Where(child2 => ...)
.Select(child2 => new
{ // etc.
})
}),
});
这是完全类型安全的。 select 任何你没有的属性都是不可能的。在限制范围内,您甚至可以创建新属性:
.Select(child4 => new
{
FullName = child4.FirstName + child4.LastName,
Age = (int)(Today - child4.BirthDay).TotalYears,
});
顺便说一句,Queryable.Include 有一个类型安全的版本,你不能在 VB 中使用它吗?
总结:
For queries use Select. Select only properties you plan to use
Only use Include if you plan to change / remove the fetched item
这是型号:
Public Class Parent
Public Property Name As String
Public Property ID As Integer
Public Property child1 As New List(Of Child1)
End Class
Public Class Child1
Public Property ID As Integer
Public Property Name As String
Public Property Child2 As New List(Of Child2)
End Class
Public Class Child2
Public Property ID As Integer
Public Property Name As String
Public Property Child3 As New List(Of Child3)
End Class
Public Class Child3
Public Property ID As Integer
Public Property Name As String
Public Property Child4 As New List(Of Child4)
End Class
Public Class Child4
Public Property ID As Integer
Public Property Name As String
End Class
要在我一直使用的查询中包含所有 table:
TheDataContext.Parent.Include("Child1").Include("Child1.Child2").Include("Child1.Child2.Child3").Include("Child1.Child2.Child3.Child4").Where(some condition).ToList()
所以这太乱了,而且没有类型检查。如果我只需要深入一个 child table 的话,我确实学习了以下作品:
TheDataContext.Parent.Include(NameOf(Child1)).Where(some condition).ToList()
但是如果我像这样在下一个 child 级别尝试应用 'NameOf' 技术
.Include(NameOf(Child1.Child2))...
在运行时失败,说 Parent 没有声明名称为 Child1.Child2 的导航 属性,回想起来这是有道理的,但我不知道如何解决这个问题。如果我使用
.Include(NameOf(Parent.Child1.Child2))...
编辑说引用 non-shared 成员需要 object 引用失败,我理解,但我不认为这是解决方案,我的模型不应该使用共享。
这个 SO Post 建议在 Include 中使用一个 lambda,Select 包含另一个 lambda,我在 C# 中理解,但在 vb.net 中,.include 似乎没有甚至允许第一个 lambda。以下在编辑器中失败:
.include(function(x) x.Child1) or anything else in the lambda
也许我缺少一个导入,但除此之外我根本不知道在 vb.net 中执行的语法在 SO post.[=19 中看起来很优雅=]
如何将 children 的所有级别转换为类型检查?
在应用以下建议和答案后添加:
不要忽视 Harald Coppoolse 的出色回答,这就是我将 string-based.Include's 转换为带有类型检查的 .Include 的方式。虽然这在技术上是我的问题的答案,但正如 Harald 的回答所示,答案不一定是正确的答案,因为可能有一个重要的问题没有被问到。
TheDataContext.Parent.Include(Function(a) a.child1.Select(Function(b) b.child2.Select(Function(c) c.child3.Select(Function(d) d.child4)))).ToList()
我的答案是在 C# 中,但这个想法也适用于 VB。我想你已经明白要点了。
使用 Select
而不是 Include
。 Select 是完全类型安全的
数据库查询中速度较慢的部分之一是从数据库管理系统到本地计算机的数据传输。将此数据限制为仅您实际计划使用的数据是明智的。
您使用 Include 传输的数据比您使用的多。例如,您的 Child1 有一个 ID。 Child1 的所有 Child2 Children 都有一个等于 Child1 的 Id 的外键 Child1Id。
所以如果你 select Child1 的 Id 为 4,而这个 Child1 有 100 Children,所有这些 Children 将有一个等于 4 的外键 Child1Id。因此你正在转移Child1 的主键值的 101 倍,而您已经知道它们都将具有相同的值。
如果您使用 Select
而不是 Include
,您将能够 select 只有您真正打算使用的属性。 Select
的另一个副作用是它是完全类型安全的,这将解决您的问题。
根据我使用 Entity Framework 的经验,如果我打算更改获取的值,我只会使用 Include
。 DbContext 只能在获取后更改值(否则您将不得不使用变通方法)。
因此,如果您想要所有(或部分)Parents 及其所有 Children、GrandChildren 等,并且您希望其类型安全,请执行以下操作:
var parentsWithTheirDescendants = myDbContext.Parents
.Where(parent => ...)
.Select(parent => new
{
// select only the properties you plan to use
Id = parent.Id,
Name = parent.Name,
Children = parent.Child1s
.Where(child1 => ...) // only if you don't want all Children
.Select(child1 => new
{
// again: select only the properties you plan to use
Id = child1.Id,
Name = child1.Name
// not needed:
// ParentId = child1.ParentId
GrandChildren = child1.Child2s
.Where(child2 => ...)
.Select(child2 => new
{ // etc.
})
}),
});
这是完全类型安全的。 select 任何你没有的属性都是不可能的。在限制范围内,您甚至可以创建新属性:
.Select(child4 => new
{
FullName = child4.FirstName + child4.LastName,
Age = (int)(Today - child4.BirthDay).TotalYears,
});
顺便说一句,Queryable.Include 有一个类型安全的版本,你不能在 VB 中使用它吗?
总结:
For queries use Select. Select only properties you plan to use
Only use Include if you plan to change / remove the fetched item