2014-10-04 5 views
0

Итак, я уже знаю, что код, как это делает все, что в памяти:Entity Framework 6: Навигационные свойства Smart?

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, но он просто создает команду в формате expression-treeable, так что последующие предложения будут встроены в команду SQL. Например, то же самое утверждение, как и раньше, будет теперь возвращает IQueryable, на котором мы можем добавить пункт Где:

SomeDbObject.QueryableSomeNavigationProperty.Where(x => x.IsSomeBool); 

Теперь вопрос в том, что произойдет, если я хочу запросить другое свойство навигации в ИНЕКЕ , Например:

SomeDbObject.QueryableSomeNavigationProperty.Where(x => SomeDbObject.AnotherNavigationProperty.Any()); 

Итак, мне нужно, чтобы сделать еще один метод, который возвращает IQueryable для SomeDbObject.AnotherNavigationProperty? Или EF делает правильную вещь здесь и строит ее в SQL-заявление?

Я могу уточнить, если необходимо, но я думаю, что это покрывает суть того, что я ищу.

Спасибо!

+1

Какое у вас доказательство, что вышеуказанные два утверждения выполнены в памяти? – SnareChops

+0

Поиск по этому вопросу в StackOverflow. На данный момент у меня нет ссылок. – twitchax

+0

Отметьте IntelliTrace, чтобы узнать, что делают ваши EF querie s –

ответ

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); 
     } 
    } 
} 
+1

Кроме того, я проверил все это с помощью LINQPad, который дает похожие результаты: http://www.linqpad.net/. – twitchax

Смежные вопросы