2012-04-09 14 views
59

tengo esta consulta LINQ:LINQ a Entidades no reconoce el método 'System.String Formato (System.String, System.Object, System.Object)'

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 
        }; 
} 

Tiene problemas sin embargo. Estoy tratando de crear tareas. Para cada nueva tarea cuando configuro el texto del enlace en una cadena constante como "Hola", está bien. Sin embargo, más arriba estoy tratando de construir el texto de enlace de la propiedad usando las propiedades de la factura.

consigo este error:

base {System.SystemException} = {"LINQ to Entities does not recognize the method 'System.String Format(System.String, System.Object, System.Object)' method, and this method cannot be translated into a store expression."}

Alguien sabe por qué? ¿Alguien sabe una forma alternativa de hacer esto para que funcione?

+0

Sí, perdidas que fuera originalmente – AnonyMouse

+0

posible duplicado de [LINQ a Entidades no reconoce el método 'System.String ToString()' método] (http://stackoverflow.com/questions/4121863/linq- to-entities-does-not-recognition-the-method-system-string-tostring-method) –

Respuesta

115

Entity Framework está intentando ejecutar su proyección en el lado de SQL, donde no hay equivalente a string.Format. Use AsEnumerable() para forzar la evaluación de esa parte con Linq to Objects.

base on the previous answer os he dado yo reestructurar su consulta como esta:

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 utilice las entidades relacionadas en la consulta (Organisation.Name) Asegúrese de añadir el buen Include a su consulta, o materializa específicamente aquellas 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 
       }); 
+0

Además del hecho de que la parte select-new-task no puede pasar del lado del servidor debido a la traducción del árbol de expresiones, también debe ser señaló que no es deseable hacerlo. Presumiblemente, desea que las tareas se creen en el lado del cliente. Por lo tanto, la separación de la consulta y la creación de tareas podría ser aún más explícita. – Tormod

+3

También recomendaría seleccionar un tipo anónimo que solo tenga el número de factura y el nombre de organización necesarios en él. Si la entidad de facturas es grande, la selección i con la AsEnumerable subsiguiente retirará cada columna aunque solo esté utilizando dos. – Devin

+1

@Devin: Sí, estoy de acuerdo, de hecho eso es exactamente lo que está haciendo el segundo ejemplo de consulta. – BrokenGlass

14

IQueriable deriva de IEnumerable, el parecido principal es que cuando haga su consulta se ha publicado en el motor de base de datos en que es lang uage, el momento delicado es cuando le dices a C# que maneje los datos en el servidor (no al lado del cliente) o que le indique a SQL que maneje los datos.

Básicamente, cuando dice IEnumerable.ToString(), C# obtiene la recopilación de datos y llama a ToString() en el objeto. Pero cuando dices IQueriable.ToString() C# le dice a SQL que llame a ToString() en el 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 a través de debe ser construida en la memoria antes de que C# aplique los filtros.

La manera más eficiente de hacerlo es hacer la consulta como IQueriable con todos los filtros que puede aplicar.

Y luego, créelo en la memoria y haga que los datos se formateen 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") 
                }); 
Cuestiones relacionadas