2012-04-19 13 views
9

¿Es posible realizar un grupo por un objeto?Entity Framework GroupPor un objeto o ComplexType

from item in context.Items 
group item by item.MyObject 
select ... 

Dónde Item.MyObject es un objeto simple como:

public class MyObject { 
    public int SomeValue { get; set; } 
    public string SomeName { get; set; } 
    public string SomeOtherProperty { get; set; } 
} 

Obviamente, podría hacer lo siguiente:

from item in context.Items 
group item by new { item.SomeValue, item.SomeName, item.SomeOtherProperty } 
select ... 

Sin embargo, cuando la agrupación por objetos con una gran cantidad de propiedades, este enfoque es tedioso y propenso a errores.

El código anterior da como resultado una NotSupportedException con el siguiente mensaje: "El tipo de selector de tecla para la llamada al método 'GroupBy' no es comparable en el proveedor de la tienda subyacente". Anular Iguales y GetHashcode no tiene ningún efecto. Supongo que el problema real es que el marco de la entidad no sabe cómo expresar el SQL ...

+0

¿'MyObject' es parte del modelo EF? Puedo hacer algo similar con una propiedad de navegación en 'MyObject' (donde MyObject: Item es n: 1). El SQL generado es horrible, pero al menos funciona. –

+0

Es en realidad un ComplexType. En resumen, sí, EF conoce el tipo de objeto. Como todas las propiedades existen realmente en la misma tabla, esperaría que el SQL resultante fuera mucho más limpio que si estuviera usando una propiedad de navegación (¡si pudiera hacerlo funcionar, por supuesto!) – mindlessgoods

Respuesta

7

Lastimosamente, no, no puedes hacer eso. ComplexType parece prometedor, y parece ser un buen "grupo de columnas", pero el equipo de EF simplemente lo diseñó para otro uso completamente y simplemente faltan muchas características relacionadas con su uso como "grupo de columnas".

Por lo que mi conocimiento abarca, el "Obviamente, yo podría hacer lo siguiente:" es la única forma posible. Solo tiene que definir la agrupación por sub columna o mediante una proyección de algún tipo que la base de datos sabrá cómo comparar.

El problema central es que el equipo EF ve el ComplexType no como un grupo de columnas que le permiten poner en orden sus entidades con muchos campos, pero, para ellos, se trata de otro tipo de entidad, tipo de un transitorio. ComplexType está destinado a representar objetos que se devuelven, por ejemplo, de un procedimiento almacenado (o funciones de devolución de tabla) que devuelve un conjunto de resultados de 5 columnas que no se pueden asignar a ninguna tabla/clase existente. Por ejemplo, imagine que tiene un cliente de tabla/clase que contiene 50 columnas/campos, la mayoría de ellos no son nulos y representan algunos datos confidenciales. Crea un procedimiento almacenado que toma una ID del cliente y devuelve su {nombre, dirección, teléfono de contacto, tamaño de calzado}. Obviamente, al configurar esos resultados recortados, no puede descodificarlos/asignarlos a la clase Cliente que tiene toneladas de campos que no pueden ser nulos. Entonces, ¿cómo se representa el resultado establecido en EF?

La primera opción, bastante coja, es simplemente ignorar EF y hablar directamente con la base de datos y leer/traducir/descomprimir las filas/columnas a mano. Bueno, sabemos que SqlClient, podemos hacer eso.

La segunda opción, fea, es definir en EF una tabla/vista secundaria, por ejemplo, CustomerView, que contendrá solo las columnas {name, address, contact phone, shoe size} y correlacionará el resultado del procedimiento almacenado lo. Funcionaría muy bien, pero si por accidente 'generar base de datos' forma el modelo EF, también obtendrá esa tabla extra no utilizada ...

La tercera opción para el rescate es el Tipo Complejo. En lugar de tratar de crear una tabla de sombras o una vista, simplemente le dice al EF que "hay un tipo de datos adicional" que nunca se correlacionará con su propia tabla, y nunca tendrá una PrimaryKey definida, y la EF para su conveniencia asocie a una clase como cualquier otro objeto de datos. Ahora, teniendo dicho tipo, puede devolverlo de los procedimientos y recuperarlo como un objeto muy bien escrito, puede pasarlo como parámetro del procedimiento, etc. Pero: no puede continuar por sí mismo. No tiene una asignación de tabla propia ni claves propias propias.Tiene sin identidad.

Es genial, pero bueno, ¿por qué quería que ese tipo de datos volviera de un procedimiento? Probablemente, el procedimiento procesado o generado algunos datos, y le gustaría almacenarlos en alguna tabla. No ha definido una entidad normal completa, por lo que eso es solo un "paquete de datos", por lo que probablemente escriba ese valor de resultado en esa tabla junto con otros datos. Y esta es la razón por la cual el EF le permite mapear ComplexType a algunas columnas de una tabla: si obtuvo ese objeto de resultado temporal de un procedimiento y ahora desea escribirlo en alguna parte, no tiene que hacerlo manualmente columna por columna , solo debe asignarlo a las columnas "nombre, dirección, teléfono de contacto, tamaño del calzado" del Cliente.

El punto más importante es que el servidor de la base de datos nunca sabe que existió tal ComplexType. Como dije, no tenía identidad. Cuando se intenta hacer una consulta LINQ como

aset.Where(item => item.complexProperty == complexValue) 

entonces, el Valor complejo es un objeto de CLR que no se asigna a ninguna mesa, no tiene identidad, por lo tanto NO PK, por tanto, EF tiene completamente ni idea de cómo comprobar si el "objeto de la mano izquierda" es lo mismo que "objeto de la mano derecha". Si los objetos hubieran definido alguna identidad, podría verificar PK en el lado del servidor, o hacer una comparación de referencia de objeto en el lado del cliente, pero aquí simplemente falla.

Estoy totalmente de acuerdo en que esta es una característica aún más útil en EF que falta. Creo que sería muy fácil para el equipo de EF comparar los Tipos Complejos columna por columna, pero no lo hicieron. Simplemente no pensaron que los ComplexTypes se pueden usar para ordenar las tablas. Han significado que sea un paquete de datos transitorios para pasar a procedimientos almacenados y desde y para funciones. Lástima.

+0

una nota al margen: digo acerca de comparar , porque acabo de tropezar con eso cuando traté de filtrar/dónde sobre un tipo complejo. La agrupación es muy similar, todo se reduce a un problema de "no sé cómo comparar". Si alguien conoce soluciones temporales o extensiones de EF, estaré más que feliz de saberlo también. Por ahora, solo he encontrado artículos que dicen 'lo siento, esto todavía no está disponible, tal vez en EF5' – quetzalcoatl

+0

Gracias por la respuesta en profundidad: es otra característica que sería genial ver en futuras versiones. – mindlessgoods