Tengo esta consulta de 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
};
}
Sin embargo, tiene problemas. Estoy intentando crear tareas. Para cada nueva tarea, cuando establezco el texto del enlace en una cadena constante como "Hola", está bien. Sin embargo, arriba estoy tratando de construir el linktext de la propiedad usando las propiedades de la factura.
Me sale este error:
base {System.SystemException} = {"LINQ to Entities no reconoce el método 'System.String Format (System.String, System.Object, System.Object)', y este método no se puede traducir a una expresión de tienda". }
¿Alguien sabe por qué? ¿Alguien conoce una forma alternativa de hacer esto para que funcione?
linq
entity-framework
linq-to-entities
AnonyMouse
fuente
fuente
Respuestas:
Entity Framework está tratando de ejecutar su proyección en el lado de SQL, donde no hay equivalente a
string.Format
. ÚseloAsEnumerable()
para forzar la evaluación de esa parte con Linq to Objects.Según la respuesta anterior que le he dado, reestructuraría su consulta de esta manera:
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 });
También veo que usa entidades relacionadas en la consulta (
Organisation.Name
) asegúrese de agregar lo adecuadoInclude
a su consulta, o materialice específicamente esas propiedades para su uso posterior, es decir: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 });
fuente
IQueryable
deriva deIEnumerable
, el parecido principal es que cuando realiza su consulta, se publica en el motor de la base de datos en su idioma, el momento delgado es cuando le dice a C # que maneje los datos en el servidor (no en el lado del cliente) o que le diga a SQL que maneje datos.Básicamente, cuando dices
IEnumerable.ToString()
, C # obtiene la recopilación de datos y llamaToString()
al objeto. Pero cuando dicesIQueryable.ToString()
C # le dice a SQL que llameToString()
al objeto, pero no existe tal método en SQL.El inconveniente es que cuando maneja datos en C #, toda la colección que está buscando debe estar construida en la memoria antes de que C # aplique los filtros.
La forma más eficaz de hacerlo es realizar la consulta como
IQueryable
con todos los filtros que puede aplicar.Y luego constrúyalo en la memoria y haga el formato de datos en 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") });
fuente
Si bien SQL no sabe qué hacer con un
string.Format
, puede realizar una concatenación de cadenas.Si ejecuta el siguiente código, debería obtener los datos que busca.
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 };
Una vez que realizas la consulta, esto debería ser ligeramente más rápido que usar
AsEnumerable
(al menos eso es lo que encontré en mi propio código después de tener el mismo error original que tú). Sin embargo, si está haciendo algo más complejo con C #, aún necesitará usarloAsEnumerable
.fuente
AsEnumerable()
puede ser mucho más eficiente. EviteAsEnumerable()
yToList()
hasta que realmente desee recordar todos los resultados.