2010-12-11 7 views
7

[EDITADO: Dejé la pregunta original a continuación, con algo más de contexto y código para reproducir el problema. La versión corta siguiente contiene la esencia de la cuestión]EF4: La consulta de Entidades LINQ 2 funciona en C# pero no en VB

versión corta: la consulta a continuación lanza una System.NotSupportedException: "No se puede convertir el tipo 'System.Linq.IOrderedQueryable 1' to type 'System.Linq.IQueryable 1' LINQ a Entidades sólo admite fundición Entidad. Modelo de datos de tipos primitivos ". La excepción solo se plantea en la versión de VB.Net. Cuando se traduce a C#, no se genera ninguna excepción.

Dim doesThisCrash = From outerOrder In orders 
     Where outerOrder.ProductId = 
     (From p In products Join o In orders On p.Id Equals o.ProductId 
     Order By p.Id 
     Select p.Id).FirstOrDefault() 
     Select outerOrder 
    doesThisCrash.ToList() 

lo tanto, para hacer que se cierre, parece que necesitamos una sub consulta donde el ObjectSet originales (órdenes) se une con otro ObjectSet (productos), y ordenó. Al usar solo los pedidos o los productos configurados, no se produce un bloqueo. Al omitir el Order By, tampoco se produce un bloqueo.

Me inclino a pensar que esto es un (VB.Net) error del compilador, a menos que haya algo obvio que estoy pasando por alto aquí ...

Por ahora mi pregunta sigue en pie:

  • ¿por qué una misma consulta aparentemente exacta funciona en C# pero no en VB?
  • ¿se puede hacer que esta consulta funcione en VB.Net?

[/ EDIT]

opcional, versión más larga (pregunta original):

Mi dominio se ve muy diferente, pero me tradujo el problema a una versión más simple, con las siguientes entidades (nota: de hecho, me definido estas usando el diseñador .edmx, por lo que esta es una versión simplificada):

public class Product 
    { 
     public int Id { get; set; } 
     public string Name { get; set; } 
     public DateTime DateCreated { get; set; } 
    } 

    public class Order 
    { 
     public int Id { get; set; } 
     public int CustomerId { get; set; } 
     public int ProductId { get; set; } 
     public DateTime OrderDate { get; set; } 
    } 

    public class Customer 
    { 
     public int Id { get; set; } 
    } 

que estoy tratando de resolver una consulta LINQ a las entidades que deben buscar strucurally como este, en VB.Net :

Dim db = New SampleEntities() 
    Dim orders As IQueryable(Of Order) = db.Orders 
    Dim products As IQueryable(Of Product) = db.Products 
    Dim currentDate = DateTime.Now 

    Dim qLinq = From outerOrder In orders 
       Where outerOrder.OrderDate = currentDate AndAlso 
       outerOrder.ProductId = 
        (From p In products Join o In orders On p.Id Equals o.ProductId 
        Where o.OrderDate = outerOrder.OrderDate AndAlso 
          outerOrder.CustomerId = o.CustomerId 
        Order By p.DateCreated 
        Select p.Id).FirstOrDefault() 
       Select outerOrder 

Esto plantea una System.NotSupportedException:

"No se puede convertir el tipo 'System.Linq.IOrderedQueryable 1' to type 'System.Linq.IQueryable 1'. LINQ a Entidades sólo admite fundición modelo de entidad de datos de tipos primitivos."

Al salir de la 'ORDER BY' parte, se plantea no es una excepción.

Realmente no veo una razón por la que esta consulta no sería apoyado ... Así que decidí intentar hacer lo mismo en C#:

var qLinq = from oOut in orders 
      where oOut.OrderDate == currentDate 
        && oOut.ProductId == 
          (from p in products join o in orders on p.Id equals o.ProductId 
          where oOut.OrderDate == o.OrderDate 
          && oOut.CustomerId == o.CustomerId 
          orderby p.DateCreated 
          select p.Id).FirstOrDefault() 
        select oOut; 

para mi sorpresa, esto funciona luego traduje el C# consulta a la sintaxis método de extensión, y luego de vuelta a VB, pero tengo la misma resultados (la versión de C# funciona, la versión de VB.Net plantea la misma excepción).

Así que supongo que mi pregunta es doble:

  • ¿Por qué una apariencia exacta misma obra de consulta en C#, pero no en VB?
  • ¿se puede hacer que esta consulta funcione en VB.Net?

Para referencia, aquí están las consultas en sintaxis método de extensión:

C# versión:

 var q = orders.Where(outerOrder => 
      outerOrder.OrderDate == currentDate && 
      outerOrder.ProductId == 
      (products 
       .Join(orders, 
        f => f.Id, 
        o => o.ProductId, 
        (f, o) => new { f, o }) 
       .Where(t => t.o.OrderDate == outerOrder.OrderDate 
          && outerOrder.CustomerId == t.o.CustomerId) 
       .OrderByDescending(t => t.f.DateCreated) 
       .Select(t => t.f.Id)) 
       .FirstOrDefault()); 

VB.Net versión:

Dim q = orders.Where(Function(outerOrder) outerOrder.OrderDate = currentDate AndAlso 
          outerOrder.ProductId = (products.Join(orders, 
           Function(p) p.Id, 
           Function(o) o.ProductId, 
           Function(p, o) New With {.p = p, .o = o}). 
          Where(Function(x) x.o.OrderDate = outerOrder.OrderDate AndAlso 
            outerOrder.CustomerId = x.o.CustomerId). 
          OrderByDescending(Function(x) x.p.DateCreated). 
          Select(Function(x) x.p.Id). 
          FirstOrDefault())) 
+0

es un código que necesita gran recompensa para pasar tiempo en él :) –

+0

Sería bueno para ver la versión VB de SampleEntities – dbasnett

+0

@dbasnett que sólo se utiliza C# para describir el dominio, en realidad, he utilizado la construí -en la característica de generación de código EF. Solo son algunas clases con propiedades. – jeroenh

Respuesta

1

mover la línea Order By p.DateCreated según mi respuesta editada permite que la consulta se ejecute sin excepciones. Sin embargo, el SQL emitido es diferente, así que no creo que esté obteniendo el resultado correcto.

Dim qLinq = From outerOrder In orders 
      Let id = (From p In products 
         Order By p.DateCreated 
         Join o In orders On p.Id Equals o.ProductId 
         Where o.OrderDate = outerOrder.OrderDate AndAlso 
          outerOrder.CustomerId = o.CustomerId 
         Select p.Id).FirstOrDefault() 
      Where outerOrder.OrderDate = currentDate AndAlso 
        outerOrder.ProductId = id 
      Select outerOrder 
+0

Su respuesta modificada fue la primera que funcionó. Todavía creo que esto también es un error en el compilador de VB, pero te estoy otorgando la recompensa por ser el primero con una respuesta correcta. Si pudiera compartir la recompensa con los demás, lo haría porque todas las soluciones funcionan. – jeroenh

-4

hi esto puede hacerse funcionar bastante fácil ...

En lugar de IQueryable

Dim orders As IQueryable(Of Order) = db.Orders

utilizar una lista

Dim orders as List(of Order) = db.Orders.ToList() 
+3

Lo siento, pero esta es una idea horrible. Al llamar a db.Orders.ToList(), está leyendo la totalidad de la tabla Orders en la memoria y luego utilizando LINQ-to-Objects para consultarla. –

+0

De acuerdo con James Kovacs. Mala idea. ¿Qué pasa si la tabla contiene millones de registros? – jeroenh

+0

pero funciona :) no, ustedes tienen razón, no pensaron en eso –

2

creo que el siguientes trabajos, el SQL es feo:

Dim qLinq = From outerOrder In orders 
       Where outerOrder.OrderDate = currentDate AndAlso 
       outerOrder.ProductId = 
        (From x In (From p In products 
           Join o In orders On p.Id Equals o.ProductId 
           Where o.OrderDate = outerOrder.OrderDate AndAlso 
             outerOrder.CustomerId = o.CustomerId 
           Select p.Id) 
        Order By x).FirstOrDefault() 
       Select outerOrder 
+0

Su respuesta también funciona. Si pudiera, habría compartido parte de la recompensa, pero por desgracia eso no es posible ... – jeroenh

2

¿Funciona?

Dim a = From p In products Join o In orders On p.Id Equals o.ProductId 
      Order By p.DateCreated 
      Select New With {.id = p.Id, .OrderDate = o.OrderDate, .CustomerId = o.CustomerId, .DateCreated = p.DateCreated} 

    Dim b = From outerOrder In orders _ 
      Where outerOrder.OrderDate = currentDate _ 
      AndAlso outerOrder.ProductId = _ 
      (From x In a Where x.OrderDate = outerOrder.OrderDate _ 
      AndAlso x.CustomerId = outerOrder.CustomerId _ 
      Select x.id).FirstOrDefault 
+0

Tu respuesta también funciona. Si pudiera, habría compartido parte de la recompensa, pero por desgracia eso no es posible ... – jeroenh

Cuestiones relacionadas