O membro do tipo especificado 'Data' não é compatível com LINQ to Entities Exception

105

Recebi uma exceção ao implementar as seguintes instruções.

 DateTime result;
 if (!DateTime.TryParse(rule.data, out result))
     return jobdescriptions;
 if (result < new DateTime(1754, 1, 1)) // sql can't handle dates before 1-1-1753
     return jobdescriptions;
 return jobdescriptions.Where(j => j.JobDeadline.Date == Convert.ToDateTime(rule.data).Date );

Exceção

The specified type member 'Date' is not supported in LINQ to Entities. Only initializers, entity members, and entity navigation properties are supported.

Eu sei o que significa a exceção, mas não sei como me livrar dela. Qualquer ajuda?

nebulosa
fonte
Isso está no EF6 e inferior. Suportes de núcleo EF .Date.
Gert Arnold

Respostas:

102

LINQ to Entities não pode converter a maioria dos métodos .NET Date (incluindo a conversão usada) em SQL, pois não há SQL equivalente.

A solução é usar os métodos Date fora da instrução LINQ e, em seguida, passar um valor. Parece que Convert.ToDateTime (rule.data) .Date está causando o erro.

Chamar Date em uma propriedade DateTime também não pode ser convertido em SQL, portanto, uma solução alternativa é comparar as propriedades .Year .Month e .Day que podem ser convertidas para LINQ, pois são apenas inteiros.

var ruleDate = Convert.ToDateTime(rule.data).Date;
return jobdescriptions.Where(j => j.Deadline.Year == ruleDate.Year 
                       && j.Deadline.Month == ruleDate.Month 
                       && j.Deadline.Day == ruleDate.Day);
Judo
fonte
6
E quanto a j => j.JobDeadline.Date?
nebulosa
1
Date é uma propriedade em JobDeadline? Isso não deve causar um erro por si só - talvez um conflito de nomenclatura (mas não tenho certeza disso). Se a linha ainda estiver causando um erro, basta renomeá-la DeadlineDate ou similar.
Judô
1
A data é propriedade em JobDeadline. JobDeadline é o tipo DateTime do qual desejo extrair Date.
nebulosa
Então, para trabalhar isso no LINQ, você precisa apenas comparar a propriedade JobDeadline, por exemplo, j.JobDeadline> ruleDate. Isso precisa de alguns testes, mas pode funcionar. Como alternativa, compare as três propriedades de .Month .Day e .Year (j.Deadline.Year == ruleDate.Year && j j.Deadline.Month == ruleDate.Month && j.Deadline.Day == ruleDate.Day). Não é elegante, mas funciona porque são apenas números inteiros.
Judô
Hmm. Essa ideia funciona. Sujo, mas funciona. Se você escrever como resposta, posso marcar como correto.
nebulosa
230

Você pode usar o método TruncateTime de EntityFunctions para obter uma tradução correta da Datepropriedade em SQL:

using System.Data.Objects; // you need this namespace for EntityFunctions

// ...

DateTime ruleData = Convert.ToDateTime(rule.data).Date;
return jobdescriptions
    .Where(j => EntityFunctions.TruncateTime(j.JobDeadline) == ruleData);


Update: EntityFunctions está obsoleto no EF6, UseDbFunctions.TruncateTime

Slauma
fonte
Bem, eu percebi que ruleDataé DateTimetipo e j.JobDeadlinetruncou o tempo. Não parece certo. Não obteve exceção, mas também não obteve o resultado esperado.
nebulosa
@aneal: retorna todos os registros que JobDeadlinetenham a mesma data que rule.data, não importando a hora do dia . Não é isso que você deseja alcançar com sua consulta na pergunta? Por que não parece certo?
Slauma
1
+1 e eu concordo com acima, é definitivamente a melhor resposta para 99% das implementações
Jim Tollan
26
Note que EntityFunctionsestá obsoleto no EF6, você deve usar agora DbFunctions.
Julien N
2
O namespace para DbFunctions em> EF6 é System.Data.Entity: msdn.microsoft.com/en-us/library/Dn220142(v=VS.113).aspx
GraehamF
39

Para EF6, use DbFunctions.TruncateTime (mydate).

KingOfHypocrites
fonte
9

"EntityFunctions.TruncateTime" ou "DbFunctions.TruncateTime" no ef6 está funcionando, mas há alguns problemas de desempenho no Big Data.

Acho que a melhor maneira é agir assim:

DateTime ruleDate = Convert.ToDateTime(rule.data);

DateTime  startDate = SearchDate.Date;

DateTime  endDate = SearchDate.Date.AddDay(1);

return jobdescriptions.Where(j.Deadline >= startDate 
                       && j.Deadline < endDate );

é melhor do que usar partes da data para. porque a consulta é executada mais rapidamente em dados grandes.

Mahdi Shahbazi
fonte
1 para esta resposta. EntityFunctions.TruncateTime(posteriormente substituídos por DbFunctions.TruncateTime) são implementados pela conversão para SQL, onde a data e hora é convertida em uma string e é truncada. Isso torna a execução da consulta significativamente mais lenta, em proporção ao número de registros processados.
urig
3

Precisa incluir using System.Data.Entity;. Funciona bem mesmo comProjectTo<>

var ruleDate = rule.data.Date;
return jobdescriptions.Where(j => DbFunctions.TruncateTime(j.Deadline) == ruleDate);
Omkar
fonte
Como já foi respondido aqui
Gert Arnold
1

O que isso significa é que LINQ to SQL não sabe como transformar a Datepropriedade em uma expressão SQL. Isso ocorre porque a Datepropriedade da DateTimeestrutura não tem analogia no SQL.

Adam Robinson
fonte
1

Funcionou para mim

DateTime dt = DateTime.Now.Date;
var ord = db.Orders.Where
      (p => p.UserID == User && p.ValidityExpiry <= dt);

Fonte: Fóruns Asp.net

MRT2017
fonte
0

Eu tenho o mesmo problema, mas trabalho com DateTime-Ranges. Minha solução é manipular o horário de início (com qualquer data) para 00:00:00 e o horário de término para 23:59:59. Portanto, não devo mais converter meu DateTime para Date, mas ele permanece DateTime.

Se você tiver apenas um DateTime, também pode definir o horário de início (com qualquer data) para 00:00:00 e o horário de término para 23:59:59. Em seguida, você pesquisa como se fosse um intervalo de tempo.

var from = this.setStartTime(yourDateTime);
var to = this.setEndTime(yourDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);

Você também pode fazer isso com DateTime-Range:

var from = this.setStartTime(yourStartDateTime);
var to = this.setEndTime(yourEndDateTime);

yourFilter = yourFilter.And(f => f.YourDateTime.Value >= from && f.YourDateTime.Value <= to);
Peter70
fonte
0

você pode obter Enum como:

DateTime todayDate = DateTime.Now.Date; var check = db.tableName.AsEnumerable().Select(x => new
        {
            Date = x.TodayDate.Date
        }).Where(x => x.Date == todayDate).FirstOrDefault();
Omid Soleiman
fonte
Isso é efetivamente enumerar todo o conteúdo da tabela de antemão para aplicar o filtro de data ... parece ser uma ideia potencialmente muito ruim!
Javier Rapoport
0

Como foi apontado por muitos aqui, o uso da função TruncateTime é lento.

A opção mais fácil, se possível, é usar o EF Core. Ele pode fazer isso. Se você não puder, uma alternativa melhor para truncar é não alterar o campo consultado, mas modificar os limites. Se você estiver fazendo uma consulta normal do tipo 'entre', em que os limites inferior e superior são opcionais, o seguinte fará o truque.

    public Expression<Func<PurchaseOrder, bool>> GetDateFilter(DateTime? StartDate, DateTime? EndDate)
    {
        var dtMinDate = (StartDate ?? SqlDateTime.MinValue.Value).Date;
        var dtMaxDate = (EndDate == null || EndDate.Value == SqlDateTime.MaxValue.Value) ? SqlDateTime.MaxValue.Value : EndDate.Value.Date.AddDays(1);
        return x => x.PoDate != null && x.PoDate.Value >= dtMinDate && x.PoDate.Value < dtMaxDate;
    }

Basicamente, em vez de reduzir PoDate de volta para apenas a parte da data, incrementamos o limite superior da consulta e o usuário <em vez de <=

Statler
fonte