Entity Framework 6 related entity where datetime clause doesn't translate to SQL

c# entity-framework-6

Question

How do I get the date filter into the generated SQL query without access to database context?

EF makes some very expensive queries. I don't cast or use the results as IEnumerable, so I would expect EF to make an efficient query. I would expect it to use the start and stop times to filter the results in SQL.

EF translates the following

context.Channels
  .First(ch => ch.Channel_ID == id)
  .ChannelValues
  .Where(cv => start < cv.ValueTime && cv.ValueTime <= stop);

into

SELECT TOP (1) 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ChannelType_ID] AS [ChannelType_ID], 
    [Extent1].[Name] AS [Name], 
    FROM [dbo].[Channel] AS [Extent1]
    WHERE [Extent1].[Channel_ID] = @p__linq__0
p__linq__0: '1' (Type = Int32, IsNullable = false)

SELECT 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ValueTime] AS [ValueTime], 
    [Extent1].[Value] AS [Value]
    FROM [dbo].[ChannelValue] AS [Extent1]
    WHERE [Extent1].[Channel_ID] = @EntityKeyValue1
EntityKeyValue1: '1' (Type = Int32, IsNullable = false)

The first SQL query I'm happy with, but the second should create a query similar to what the following does

context.ChannelValues
.Where(cv => cv.Channel_ID == id && start < cv.ValueTime && cv.ValueTime <= stop);

it results in

SELECT 
    [Extent1].[Channel_ID] AS [Channel_ID], 
    [Extent1].[ValueTime] AS [ValueTime], 
    [Extent1].[Value] AS [Value]
    FROM [dbo].[ChannelValue] AS [Extent1]
    WHERE ([Extent1].[Channel_ID] = @p__linq__0) AND (@p__linq__1 < [Extent1].[ValueTime]) AND ([Extent1].[ValueTime] <= @p__linq__2)
p__linq__0: '1' (Type = Int32, IsNullable = false)
p__linq__1: '7/1/2018 12:00:00 AM' (Type = DateTime2, IsNullable = false)
p__linq__2: '7/23/2018 11:45:00 AM' (Type = DateTime2, IsNullable = false

Where I actually need this I don't have access to the the DatabaseContext.

1
0
8/23/2018 2:03:17 PM

Accepted Answer

I don't think the second part of the query is getting passed to the SQL at all.

This tells SQL to return all the ChannelValues with the Channel_ID equal to id.

context.Channels
  .First(ch => ch.Channel_ID == id)
  .ChannelValues

The second where clause is then executed in memory.

.Where(cv => start < cv.ValueTime && cv.ValueTime <= stop);

To execute all of this in the database you will need to do something like this:

context.Channels
  .Where(ch => ch.Channel_ID == id)
  .SelectMany(x => x.ChannelValues)
  .Where(cv => start < cv.ValueTime && cv.ValueTime <= stop)

Though I don't know if this answers you question how to do this without access to the database context

1
8/23/2018 2:48:55 PM

Popular Answer

"EF makes some very expensive queries" mmm you have control of how you would like to use the framework with its limitation*, if you know something is bad but still do it that way its not EF fault.

You could do it exactly the way you have.

i.e. if you did this with raw sql how would you do it.

Then simply do the same in EF.

var channel = context.Channels.First(x=> x.Channel_ID == id)
channel.ChannelValues = context.ChannelValues.Where(x => x.Channel_ID == id 
                                             && start < x.ValueTime 
                                             && x.ValueTime <= stop
                              ).ToList();


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow