Usando Linq para ObjectDataSource: Como transformar datetime usando ToShortTimeString?
-
19-08-2019 - |
Pergunta
Eu estou acessando uma classe de negócios usando um ObjectDataSource e tentando produzir uma saída que faz sentido para o usuário. Os valores de retorno descrever uma classe (como na sala de aula e ensino, não de software). Eu gostaria de mostrar o tempo da classe como uma gama assim: "09h00-10:00"
Este é o Linq consulta que estou usando para puxar os dados:
return classQuery.Select(p => new SelectClassData
{
ClassID = p.ClassID,
Title = p.Title,
StartDate = p.StartDate.ToShortDateString(),
EndDate = p.EndDate.ToShortDateString(),
TimeOfClass =
p.StartDate.ToShortTimeString() + " - " +
p.EndDate.ToShortTimeString()
}).ToList();
Como você pode ver, eu codificar o início e término nas datas inicial e final, mesmo que estes poderiam ser potencialmente em datas diferentes.
Quando eu executar esse código eu recebo:
"Não foi possível traduzir a expressão 'p.EndDate.ToShortTimeString ()' em SQL e não poderia tratá-lo como uma expressão local."
Eu sei que eu estou projetando os resultados, mas, sendo novo para Linq, eu tinha assumido que o C # chamada para ToShortTimeString aconteceu após a projeção. Alguém pode me ajudar a descobrir como obter a string que estou procurando?
Solução
A razão é a consulta está sendo usado em LINQ to SQL. LINQ to SQL trata consultas como árvores de expressão. Tem mapeamentos definidos para alguns métodos (por exemplo, Contains
) mas desde que realmente não executá-los, ele não pode trabalhar em métodos arbitrários. Ele analisa a consulta e envia-lo para o servidor SQL. O equivalente a consulta será executada como uma instrução SQL no servidor de banco de dados e o resultado vai voltar. O problema é ToShortTimeString()
não tem uma tradução SQL equivalente em LINQ to SQL. O truque usado aqui é para buscar dados de servidor SQL e chamar o método no lado do cliente (AsEnumerable
vai fazer isso).
return classQuery.Select(p => new { p.ClassID, p.Title, p.StartDate, p.EndDate })
.AsEnumerable()
.Select(p => new SelectClassData {
ClassID = p.ClassID,
Title = p.Title,
StartDate = p.StartDate.ToShortDateString(),
EndDate = p.EndDate.ToShortDateString(),
TimeOfClass = p.StartDate.ToShortTimeString() + " - " + p.EndDate.ToShortTimeString() })
.ToList();
Outras dicas
Eu gosto de resposta de Mehrdad muito. Ele não só resolve o problema, ele também me ensina algo sobre Linq também. Obrigado!
Eu mantive batendo contra o problema, embora, e veio com uma abordagem diferente, que vou descrever aqui no caso de alguém tropeçar sobre esta questão quer considerar. Meu código SQL Linq agora lê-se:
return classQuery.Select(p => new SelectClassData
{
ClassID = p.ClassID,
Title = p.Title,
sDate = p.StartDate,
eDate = p.EndDate
}).ToList();
Note que sdate e eDate estão agora DateTime objetos em vez de strings. No objeto "SelectClassData", eu simplesmente mudou a declaração para que o acesso às variáveis ??StartDate, EndDate e TimeOfClass passar por um getter propriedade:
public class SelectClassData
{
public int ClassID { get; set; }
public string Title { get; set; }
public DateTime sDate { get; set; }
public DateTime eDate { get; set; }
public string StartDate { get { return GetSDate(); } }
public string EndDate { get { return GetEDate(); } }
public string TimeOfClass { get { return GetTimeOfClass(); } }
protected string GetSDate()
{
return sDate.ToShortDateString();
}
protected string GetEDate()
{
return eDate.ToShortDateString();
}
protected string GetTimeOfClass()
{
return sDate.ToShortTimeString() + " - " + eDate.ToShortTimeString();
}
}
Isto é, eu definir o sdate e eDate através da LinqToSql mas fazer o "ToShortTimeString" e transformações "ToShortDateString" após a recuperação Linq, implementando-o na classe de dados de destino.