About your first question, yes, this will load all records from the database to the client.
When working with tables with a lot of records, as you have mentioned you can use .Skip(() => ...)
and Take(() => ....)
About the memory and the other linq epxresions like .Where, SingleOrDefault, FirstOrDefault and so on ... there is the overhead of the translation from query expression to SQL query, but EF already cache those and reuses and parsed queries when it can. So it is not that bad.
About the query expression and more information about them you can check: http://www.tutorialsteacher.com/linq/linq-tutorials
About EF more here: http://www.entityframeworktutorial.net/
About EF query performance:
EF will cache queries and reuse them as i've mentioned already.
Also creates store procedures and use them instead of RAW sql queries. This can be easily observed with SQL Server Profiler
About db context performance
One of the benefits of the DbContext is the ChangeTracker which will track any Entity changes during its lifetime (until disposed) and this is ok if you're planing of making some modifications/updates of specific entities, but when your making 'readonly' queries you can optimize the context a bit.
The DbContext
has .Configuration
property with the following settings.
AutoDetectChangesEnabled
enable/disable the change tracking
LazyLoadingEnabled
enable/disable lazy loading of navigation properties.
More info about lazy loading: http://www.entityframeworktutorial.net/EntityFramework4.3/lazy-loading-with-dbcontext.aspx
ProxyCreationEnabled
enables/disable the creation of runtime wrappers for the entities. This is related with the navigational properties in you entities. Shortly when you have relation One To Many (lets say person with many friends) when accessing the .Firends property with enabled LazyLoading EF will translate this to JOIN Query and will fetch and materialize all friends for the current entity, and this is possible because of Proxy class which overrides the .Friends Property behaviour.
So when you do only readonly queries you can disable those settings. Keep in mind that disabling the LazyLoading will result in not materialized navigation properties. To fix this you have to join/include those in the original query.
This can be achinved using the some of the approaches described here: https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx
About query materialization
You've discovered two of the possible ways.
Using .ToList() or pagination using Skip and Take
You can also foreach
the query which will create SQL CURSOR.
More about query materialization: https://msdn.microsoft.com/en-us/library/bb738633(v=vs.110).aspx
This are some overall hints. Further you can check the links in the post, i think they are good starting point.
Summary:
From my experience ORM's are perfect for new projects because they are easy for modification and can be approached from more team members (is closer to C# then SQL). But in the end some portion of the queries are rewritten in SQL mainly because it is still faster to run sp from the server and EF sometimes creates complex queries which is best to be avoided.