简体   繁体   中英

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

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.

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

"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.

ie 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();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM