Eu tenho esta consulta linq:
private void GetReceivedInvoiceTasks(User user, List<Task> tasks)
{
var areaIds = user.Areas.Select(x => x.AreaId).ToArray();
var taskList = from i in _db.Invoices
join a in _db.Areas on i.AreaId equals a.AreaId
where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId)
select new Task {
LinkText = string.Format(Invoice {0} has been received from {1}, i.InvoiceNumber, i.Organisation.Name),
Link = Views.Edit
};
}
Porém, tem problemas. Estou tentando criar tarefas. Para cada nova tarefa, quando eu definir o texto do link para uma string constante como "Olá", tudo bem. No entanto, acima estou tentando construir o linktext de propriedade usando propriedades da fatura.
Eu recebo este erro:
base {System.SystemException} = {"LINQ to Entities não reconhece o método 'System.String Format (System.String, System.Object, System.Object)' e este método não pode ser convertido em uma expressão de armazenamento." }
Alguém sabe por quê? Alguém conhece uma forma alternativa de fazer isso para que funcione?
linq
entity-framework
linq-to-entities
AnonyMouse
fonte
fonte
Respostas:
Entity Framework está tentando executar sua projeção no lado SQL, onde não há equivalente a
string.Format
. UseAsEnumerable()
para forçar a avaliação daquela peça com Linq to Objects.Com base na resposta anterior que dei a você, reestruturaria sua consulta assim:
int statusReceived = (int)InvoiceStatuses.Received; var areaIds = user.Areas.Select(x=> x.AreaId).ToArray(); var taskList = (from i in _db.Invoices where i.Status == statusReceived && areaIds.Contains(i.AreaId) select i) .AsEnumerable() .Select( x => new Task() { LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.Organisation.Name), Link = Views.Edit });
Também vejo que você usa entidades relacionadas na consulta (
Organisation.Name
), certifique-se de adicionar o apropriadoInclude
à sua consulta ou materializar especificamente essas propriedades para uso posterior, ou seja:var taskList = (from i in _db.Invoices where i.Status == statusReceived && areaIds.Contains(i.AreaId) select new { i.InvoiceNumber, OrganisationName = i.Organisation.Name}) .AsEnumerable() .Select( x => new Task() { LinkText = string.Format("Invoice {0} has been received from {1}", x.InvoiceNumber, x.OrganisationName), Link = Views.Edit });
fonte
IQueryable
deriva deIEnumerable
, a principal semelhança é que quando você faz sua consulta, ela é postada no mecanismo de banco de dados em sua linguagem, o momento tênue é quando você diz ao C # para lidar com os dados no servidor (não no lado do cliente) ou para dizer ao SQL para lidar dados.Então, basicamente, quando você diz
IEnumerable.ToString()
, C # obtém a coleta de dados e chamaToString()
o objeto. Mas quando você diz,IQueryable.ToString()
C # informa ao SQL para chamarToString()
o objeto, mas não existe tal método no SQL.A desvantagem é que, quando você lida com dados em C #, toda a coleção que você está examinando deve ser construída na memória antes que o C # aplique os filtros.
A maneira mais eficiente de fazer isso é fazer a consulta
IQueryable
com todos os filtros que você pode aplicar.E então acumule-o na memória e faça a formatação dos dados em C #.
IQueryable<Customer> dataQuery = Customers.Where(c => c.ID < 100 && c.ZIP == 12345 && c.Name == "John Doe"); var inMemCollection = dataQuery.AsEnumerable().Select(c => new { c.ID c.Name, c.ZIP, c.DateRegisterred.ToString("dd,MMM,yyyy") });
fonte
Embora o SQL não saiba o que fazer com um,
string.Format
ele pode realizar a concatenação de strings.Se você executar o código a seguir, deverá obter os dados que procura.
var taskList = from i in _db.Invoices join a in _db.Areas on i.AreaId equals a.AreaId where i.Status == InvoiceStatuses.Received && areaIds.Contains(a.AreaId) select new Task { LinkText = "Invoice " + i.InvoiceNumber + "has been received from " + i.Organisation.Name), Link = Views.Edit };
Depois que você realmente executa a consulta, isso deve ser um pouco mais rápido do que usar
AsEnumerable
(pelo menos foi o que encontrei em meu próprio código depois de ter o mesmo erro original que você). Se você estiver fazendo algo mais complexo com C #, ainda precisará usarAsEnumerable
.fonte
AsEnumerable()
pode ser muito mais eficiente. EviteAsEnumerable()
eToList()
até que você realmente queira trazer todos os resultados para a memória.