The following is the C# code and generated SQL in a LINQ to SQL query for two cases.
using (JulianDataContext dc = new JulianDataContext(this.CurrentConnectionString))
{
#if DEBUG
dc.Log = new DebugTextWriter();
#endif
IEnumerable<UserNewsfeedDeliveryTime> temp = dc.UserNewsfeedDeliveryTimes.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
ids = temp.Select(p => p.Id).ToList();
}
SELECT [t0].[Id], [t0].[NewsfeedEmailPeriodicity], [t0].[LastSentNewsfeedEmail], [t0].[NextNewsfeedDelivery]
FROM [dbo].[UserNewsfeedDeliveryTimes] AS [t0]
WHERE ([t0].[NewsfeedEmailPeriodicity] > @p0) AND (@p1 >= CONVERT(DATE, [t0].[NextNewsfeedDelivery]))
-- @p0: Input Int (Size = -1; Prec = 0; Scale = 0) [0]
-- @p1: Input DateTime (Size = -1; Prec = 0; Scale = 0) [15-11-2012 00:00:00]
using (JulianDataContext dc = new JulianDataContext(this.CurrentConnectionString))
{
#if DEBUG
dc.Log = new DebugTextWriter();
#endif
IEnumerable<UserNewsfeedDeliveryTime> temp = dc.GetTable<UserNewsfeedDeliveryTime>();
temp = temp.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
ids = temp.Select(p => p.Id).ToList();
}
SELECT [t0].[Id], [t0].[NewsfeedEmailPeriodicity], [t0].[LastSentNewsfeedEmail], [t0].[NextNewsfeedDelivery]
FROM [dbo].[UserNewsfeedDeliveryTimes] AS [t0]
The difference between these two linq queries:
dc.UserNewsfeedDeliveryTimes
and
dc.GetTable<UserNewsfeedDeliveryTime>()
Why? Could it be that, in case 2, LINQ to SQL is retrieving all data from database and finish the query by filtering all objects in memory?
If so, how can we make keep this generic and still force all the T-SQL to be generated?
Both answers, are correct but I had to pick one, sorry! I think also it is interesting to add that in this case, since I changed to work with an IQueryable (inherits from IEnumerable), in this line:
temp = temp.Where(u => u.NewsfeedEmailPeriodicity > 0 && DateTime.Today >= u.NextNewsfeedDelivery.Value.Date);
I had two overload methods, one from the IQueryable interface and another to the IEnumerable interface.
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
public static IEnumerable<TSource> Where<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate);
So I had to convert my predicate explicitly to Expression> predicate, otherwise the IEnumerable interface method would have been picked up at compile time and, if I am not mistaken, I would get some dynamic sql exception saying the T-SQL could not have been generated.
From my understanding, IEnumerable
does not transform the original query information that IQueryable
holds. It's almost as if the cast freezes any changes to the IQueryable
query at the point of casting. If you look at MSDN, it turns out that IQueryable
inherits IEnumerable
:
http://msdn.microsoft.com/en-us/library/system.linq.iqueryable.aspx
Hence, you see this behaviour. It is important with LINQ-SQL to work with IQueryable
unless you want the query frozen at the point it is turned to an IEnumerable
.
In your first example, the where
is inclusive of the original query. The select
is not hence the query generated.
In your second example, you capture the table itself into an IEnumerable
. Any changes on top of this are done in memory on top of the original query.
When you think, the IEnumerable
version of where
will not be able to transform the original data of the IQueryable
due to the cast and how inheritance works.
When you also consider deferred loading, and how LINQ works, this seems to make sense. To me it is a big annoyance, as it can lead you into generating some terrible performing code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With