所以,我已经知道像这样的代码在内存中做了所有事情:
SomeDbObject.SomeNavigationProperty.Where(x => x.IsSomeBool);
除此之外(仍在记忆中):
SomeDbObject.SomeNavigationProperty.AsQueryable().Where(x => x.IsSomeBool);
所以,我提出了一个友好的解决方案,可以帮助我确保整个调用将作为SQL命令执行(这只是SomeDbObject上的一个属性):
public IQueryable<AnotherDbObject> QueryableSomeNavigationProperty
{
get
{
return SomeStaticContext.AnotherDbObjects.Where(x => x.ForeignKeyForSomeDbObject == this.Id);
}
}
因此,正如您所看到的,这基本上完成了Navigation属性所做的事情,但它只是以表达式可树形格式创建命令,以便后续子句将构建到SQL命令中。例如,与之前相同的语句现在将返回一个IQueryable,我们可以在其上添加Where子句:
SomeDbObject.QueryableSomeNavigationProperty.Where(x => x.IsSomeBool);
现在,问题是,如果我想在where子句中查询另一个导航属性会发生什么。例如:
SomeDbObject.QueryableSomeNavigationProperty.Where(x => SomeDbObject.AnotherNavigationProperty.Any());
那么,我是否需要为SomeDbObject.AnotherNavigationProperty创建另一个返回IQueryable的方法?或者,EF在这里做正确的事情并将其构建为SQL语句吗?
如果需要,我可以澄清,但我认为这涵盖了我所寻找的要点。
谢谢!
答案 0 :(得分:1)
好的,大家好。我运行了一系列模拟并且判决结果正在进行中。请参阅每个方案中的结果注释。注释表示SQL何时吐出! :)
希望这有助于下一个对EF6在什么时候做什么感到困惑的可怜的灵魂!
class Program
{
private static readonly Action<string> DebugWriteLine = s => System.Diagnostics.Debug.WriteLine(s);
private static readonly Action<string> WriteLine = s => { System.Console.WriteLine(s); DebugWriteLine(s); };
static void Main(string[] args)
{
Statics.Entities.Database.Log = WriteLine;
WhereClauseOnSimpleProperty();
WhereClauseOnNavigationProperty();
WhereClauseOnICollection();
WhereClauseOnIQueryable();
WhereClauseOnIQueryableWithIQueryable();
System.Console.ReadKey();
}
static void WhereClauseOnSimpleProperty()
{
WriteLine("Get objects with a where clause (simple property).");
WriteLine(" Calling: var users = entities.Users.Where(u => u.FirstName == \"Julie\");");
var users = Statics.Entities.Users.Where(u => u.FirstName == "Julie");
WriteLine(" Calling: users.ToList();");
var usersList = users.ToList();
// SQL got built and called here (NOTE: SQL call is not made until the data needs to be "realized"):
/* SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Uin] AS [Uin],
[Extent1].[ClientId] AS [ClientId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Users] AS [Extent1]
WHERE 'Julie' = [Extent1].[FirstName]
*/
WriteLine(" There are " + usersList.Count + " users.");
}
static void WhereClauseOnNavigationProperty()
{
WriteLine("Get objects with a where clause (1-to-many navigation property).");
WriteLine(" Calling: var users = Entities.Users.Where(u => u.FirstName == \"Julie\" && u.Votes.Any());");
var users = Statics.Entities.Users.Where(u => u.FirstName == "Julie" && u.Votes.Any());
WriteLine(" Calling: users.ToList();");
var usersList = users.ToList();
// SQL got built and called here (NOTE: using the ICollection navigation property on the lambda parameter "u" builds just one SQL statement):
/* SELECT
[Extent1].[Id] AS [Id],
[Extent1].[Uin] AS [Uin],
[Extent1].[ClientId] AS [ClientId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Users] AS [Extent1]
WHERE ('Julie' = [Extent1].[FirstName]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Votes] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[UserId]
))
*/
WriteLine(" There are " + usersList.Count + " users.");
}
static void WhereClauseOnICollection()
{
WriteLine("Get objects with a where clause (simple property) from an ICollection.");
WriteLine(" Calling: var users = Entities.Users.First(u => u.FirstName == \"Julie\" && u.Votes.Any());");
var user = Statics.Entities.Users.First(u => u.FirstName == "Julie" && u.Votes.Any());
// SQL got built and called here (NOTE: data is realized immediately because we are allocating a single object):
/* SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Uin] AS [Uin],
[Extent1].[ClientId] AS [ClientId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Users] AS [Extent1]
WHERE ('Julie' = [Extent1].[FirstName]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Votes] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[UserId]
))
*/
WriteLine(" Calling: var votes = user.Votes.AsQueryable().Where(v => v.VoteValue > 0);");
var votes = user.Votes.AsQueryable().Where(v => v.VoteValue > 0);
// SQL got built and called here (NOTE: there "where" clause is executed in app memory/time [it's not in the SQL call]):
/* SELECT
[Extent1].[Id] AS [Id],
[Extent1].[UserId] AS [UserId],
[Extent1].[VoteValue] AS [VoteValue]
FROM [dbo].[Votes] AS [Extent1]
WHERE [Extent1].[UserId] = @EntityKeyValue1
*/
WriteLine(" Calling: votes.ToList();");
var votesList = votes.ToList();
WriteLine(" There are " + votesList.Count + " votes.");
}
static void WhereClauseOnIQueryable()
{
WriteLine("Get objects with a where clause (1-to-many navigation property) from an IQueryable.");
WriteLine(" Calling: var users = Entities.Users.First(u => u.FirstName == \"Julie\" && u.Votes.Any());");
var user = Statics.Entities.Users.First(u => u.FirstName == "Julie" && u.Votes.Any());
// SQL got built and called here:
/* SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Uin] AS [Uin],
[Extent1].[ClientId] AS [ClientId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Users] AS [Extent1]
WHERE ('Julie' = [Extent1].[FirstName]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Votes] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[UserId]
))
*/
WriteLine(" Calling: var votes = user.QueryableVotes.Where(v => user.Votes.AsQueryable().Contains(v));");
var votes = user.QueryableVotes.Where(v => user.Votes.AsQueryable().Contains(v));
// SQL got built and called here (NOTE: this is just the "user.Votes.AsQueryable().Contains(v)" part of the query):
/* SELECT
[Extent1].[Id] AS [Id],
[Extent1].[UserId] AS [UserId],
[Extent1].[VoteValue] AS [VoteValue]
FROM [dbo].[Votes] AS [Extent1]
WHERE [Extent1].[UserId] = @EntityKeyValue1
*/
WriteLine(" Calling: votes.ToList();");
var votesList = votes.ToList();
// NOTE: EF6 dies here because it had already computed "user.Votes.Contains(v)" (see above), and that can't go into the query.
WriteLine(" There are " + votesList.Count + " votes.");
}
static void WhereClauseOnIQueryableWithIQueryable()
{
WriteLine("Get objects with a where clause (1-to-many navigation property as an IQueryable) from an IQueryable.");
WriteLine(" Calling: var users = Entities.Users.First(u => u.FirstName == \"Julie\" && u.Votes.Any());");
var user = Statics.Entities.Users.First(u => u.FirstName == "Julie" && u.Votes.Any());
// SQL got built and called here:
/* SELECT TOP (1)
[Extent1].[Id] AS [Id],
[Extent1].[Uin] AS [Uin],
[Extent1].[ClientId] AS [ClientId],
[Extent1].[FirstName] AS [FirstName],
[Extent1].[LastName] AS [LastName]
FROM [dbo].[Users] AS [Extent1]
WHERE ('Julie' = [Extent1].[FirstName]) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Votes] AS [Extent2]
WHERE [Extent1].[Id] = [Extent2].[UserId]
))
*/
WriteLine(" Calling: var votes = user.QueryableVotes.Where(v => user.QueryableVotes.Contains(v));");
var votes = user.QueryableVotes.Where(v => user.QueryableVotes.Contains(v)); // Yes, I know this is reduntant...just making sure the SQL looks right.
WriteLine(" Calling: votes.ToList();");
var votesList = votes.ToList();
// SQL got built and called here (NOTE: making all expressions true IQueryables will build the "correct" [one call to rule them all] SQL expression):
/* SELECT
[Extent1].[Id] AS [Id],
[Extent1].[UserId] AS [UserId],
[Extent1].[VoteValue] AS [VoteValue]
FROM [dbo].[Votes] AS [Extent1]
WHERE ([Extent1].[UserId] = @p__linq__0) AND ( EXISTS (SELECT
1 AS [C1]
FROM [dbo].[Votes] AS [Extent2]
WHERE ([Extent2].[UserId] = @p__linq__1) AND ([Extent2].[Id] = [Extent1].[Id])
))
*/
WriteLine(" There are " + votesList.Count + " votes.");
}
// SPECIAL NOTE: The clauses should follow these guidelines:
/*
* 1. If the condition operates on the lambda parameter, then use the ICollection navigation property to achieve one statement.
* For example: var user = Statics.Entities.Users.First(u => u.FirstName == "Julie" && u.Votes.Any());
* 2. If the condition operates on a "non-navigation" property of the lambda parameter, then use the IQueryable expression to acheive one statement.
* For example: var votes = user.QueryableVotes.Where(v => user.QueryableVotes.Contains(v));
*/
}
public partial class User
{
public IQueryable<Vote> QueryableVotes
{
get
{
return Statics.Entities.Votes.Where(v => v.UserId == this.Id);
}
}
}