121

Por varias razones que no tienen la libertad de hablar, estamos definiendo una vista en nuestro servidor de base de datos SQL 2005, así:Entity Framework y SQL Server Ver

CREATE VIEW [dbo].[MeterProvingStatisticsPoint] 
AS 
SELECT 
    CAST(0 AS BIGINT) AS 'RowNumber', 
    CAST(0 AS BIGINT) AS 'ProverTicketId', 
    CAST(0 AS INT) AS 'ReportNumber', 
    GETDATE() AS 'CompletedDateTime', 
    CAST(1.1 AS float) AS 'MeterFactor', 
    CAST(1.1 AS float) AS 'Density', 
    CAST(1.1 AS float) AS 'FlowRate', 
    CAST(1.1 AS float) AS 'Average', 
    CAST(1.1 AS float) AS 'StandardDeviation', 
    CAST(1.1 AS float) AS 'MeanPlus2XStandardDeviation', 
    CAST(1.1 AS float) AS 'MeanMinus2XStandardDeviation' 
WHERE 0 = 1 

La idea es que el Entity Framework creará una entidad sobre la base de esta consulta, que lo hace, pero genera con un error que indica lo siguiente:

Advertencia 6002: la tabla/vista 'Keystone_Local.dbo.MeterProvingStatisticsPoint' no tiene una clave principal definida. La clave se ha inferido y la definición se creó como una tabla/vista de solo lectura.

Y decide que el campo CompletedDateTime será la clave primaria de esta entidad.

Estamos utilizando EdmGen para generar el modelo. ¿Hay alguna manera de que el marco de entidad no incluya ningún campo de esta vista como clave principal?

Respuesta

220

Tuvimos el mismo problema y esta es la solución:

Para forzar marco de la entidad a utilizar una columna como clave principal, el uso ISNULL.

Para forzar que el marco de la entidad no use una columna como clave principal, use NULLIF.

Una forma fácil de aplicar esto es ajustar la instrucción de selección de su vista en otra selección.

Ejemplo:

SELECT 
    ISNULL(MyPrimaryID,-999) MyPrimaryID, 
    NULLIF(AnotherProperty,'') AnotherProperty 
    FROM (...) AS temp 
+1

Creo que esto es lo mejor que se espera. En resumen, funciona. – MvcCmsJon

+0

He intentado esto y no funciona. ¿EF Designer analiza la definición de la vista o simplemente deduce las columnas de los resultados de los datos? – sabanito

+1

¡Gracias! Funcionó perfectamente. @sabanito Creo que analiza la definición. es por eso que debe envolver específicamente las propiedades clave en IsNull(). Tengo una vista que no devuelve ningún nulo (y no puede devolver ningún valor nulo) pero debido a la forma en que se escribió la lógica, EF no pudo determinar que ese era el caso hasta que envolví las claves en IsNull(). – Rabbi

3
+0

Eso tiene sentido. Entonces, ¿hay alguna manera de definir una columna como nula o nula en una vista de la forma en que la definimos? –

+1

Lo siento, ya estoy más allá de mi nivel de experiencia en Entity Framework. :-) – RBarryYoung

+1

¿Alguien sabe cuándo se solucionará este problema? Es molesto tener que solucionar esto cuando tienes columnas no nulas que no son claves primarias. –

4

El generador de corriente Entity Framework EDM creará una clave compuesta de todos los campos no anulable en su punto de vista. Para obtener control sobre esto, deberá modificar la vista y las columnas de la tabla subyacente para que las columnas sean anulables cuando no desee que formen parte de la clave principal. Lo contrario también es cierto, como me encontré, la clave generada por EDM causaba problemas de duplicación de datos, por lo que tuve que definir una columna que aceptaba nulos como no anulable para obligar a la clave compuesta en el EDM a incluir esa columna.

+0

Tenemos el mismo problema con la PK inferida, la entidad devuelve registros duplicados y es completamente molesta. Si ejecuta 'Context.Entity.ToList()' duplica los registros, pero si ejecuta la consulta SQL generada por EF directamente (obtenida con LINQPad), no ocurre duplicación de registros. Parece ser un problema al mapear los registros de la base de datos a los objetos de entidad (POCO) devueltos, ya que el PK se deduce utilizando la lógica explicada (columnas que no admiten nulos). –

3

Para obtener una vista solo tenía que mostrar una columna de clave principal Creé una segunda vista que apuntaba a la primera y utilizaba NULLIF para hacer que los tipos fueran nulables. Esto funcionó para hacer que el EF pensara que solo había una clave primaria en la vista.

No estoy seguro si esto te ayudará, ya que no creo que el EF acepte una entidad sin la clave principal.

45

De acuerdo con @Tillito, sin embargo en la mayoría de los casos se ensuciará optimizador de SQL y no utilizará los índices correctos.

Puede ser obvio para alguien, pero me quemé horas resolviendo problemas de rendimiento con la solución Tillito.Digamos que tiene la tabla:

Create table OrderDetail 
    ( 
     Id int primary key, 
     CustomerId int references Customer(Id), 
     Amount decimal default(0) 
    ); 
Create index ix_customer on OrderDetail(CustomerId); 

y su vista es algo como esto

Create view CustomerView 
    As 
     Select 
      IsNull(CustomerId, -1) as CustomerId, -- forcing EF to use it as key 
      Sum(Amount) as Amount 
     From OrderDetail 
     Group by CustomerId 

optimizador de SQL no va a usar ix_customer índice y se llevará a cabo exploración de tabla en el índice principal, pero si en lugar de:

Group by CustomerId 

que utilizan

Group by IsNull(CustomerId, -1) 

hará que MS SQL (al menos 2008) incluya el índice correcto en el plan.

Si

+2

Esto debería ser un comentario sobre la respuesta de Tillito, no una respuesta en sí misma, ya que no proporciona una solución para la pregunta del OP. – zimdanen

+5

El chico tiene un representante de 1, no puede agregar un comentario, aún. – jrcs3

+0

Gracias por el comentario. –

7

Este método funciona bien para mí. Yo uso ISNULL() para el campo de clave principal, y COALESCE() si el campo no debe ser la clave principal, pero también debe tener un valor que no admite nulos. Este ejemplo produce un campo ID con una clave primaria no anulable. Los otros campos no son claves, y tienen (Ninguno) como su atributo Nullable.

SELECT  
ISNULL(P.ID, - 1) AS ID, 
COALESCE (P.PurchaseAgent, U.[User Nickname]) AS PurchaseAgent, 
COALESCE (P.PurchaseAuthority, 0) AS PurchaseAuthority, 
COALESCE (P.AgencyCode, '') AS AgencyCode, 
COALESCE (P.UserID, U.ID) AS UserID, 
COALESCE (P.AssignPOs, 'false') AS AssignPOs, 
COALESCE (P.AuthString, '') AS AuthString, 
COALESCE (P.AssignVendors, 'false') AS AssignVendors 
FROM Users AS U 
INNER JOIN Users AS AU ON U.Login = AU.UserName 
LEFT OUTER JOIN PurchaseAgents AS P ON U.ID = P.UserID 

si realmente no tiene una clave principal, se puede suplantar uno mediante el uso de ROW_NUMBER para generar una clave seudo que es ignorado por su código. Por ejemplo:

SELECT 
ROW_NUMBER() OVER(ORDER BY A,B) AS Id, 
A, B 
FROM SOMETABLE 
+0

Sí, terminé haciendo trampa con 'NEWID() como id', pero es la misma idea. Y hay casos de uso legítimos, si tiene una vista de solo lectura, por ejemplo. Feo, EF, feo. – ruffin

58

yo era capaz de resolver este usando el diseñador.

  1. Abra el navegador de modelos.
  2. Encuentra la vista en el diagrama.
  3. Haga clic con el botón derecho en la clave principal y asegúrese de que esté marcada la "clave de entidad".
  4. Selección múltiple de todas las claves no primarias. Use las teclas Ctrl o Shift.
  5. En la ventana Propiedades (presione F4 si es necesario para verla), cambie el menú desplegable "Clave de entidad" a False.
  6. Guardar cambios.
  7. Cierre Visual Studio y vuelva a abrirlo. Estoy usando Visual Studio 2013 con EF 6 y tuve que hacer esto para que las advertencias desaparezcan.

No tuve que cambiar mi vista para utilizar las soluciones provisionales ISNULL, NULLIF o COALESCE. Si actualiza su modelo desde la base de datos, las advertencias volverán a aparecer, pero desaparecerán si cierra y vuelve a abrir VS. Los cambios que realizó en el diseñador se conservarán y no se verán afectados por la actualización.

+8

Confirmado. Debe reiniciar VS2013 para que la advertencia desaparezca. –

+5

Ojalá pudiera votar esto unos cientos de veces más. –

+3

"¿Has intentado apagarlo y encenderlo de nuevo?" ;-) Gracias, funciona como un encanto! –

2

También recomiendo si no quiere meterse con lo que debería ser la clave principal para incorporar ROW_NUMBER a su selección y configurarlo como clave principal y establecer el resto de columnas/memebers como no primarios en el modelo.

1

Debido a los problemas mencionados anteriormente, prefiero las funciones de valores de tabla.

Si usted tiene esto:

CREATE VIEW [dbo].[MyView] AS SELECT A, B FROM dbo.Something 

crear esto:

CREATE FUNCTION MyFunction() RETURNS TABLE AS RETURN (SELECT * FROM [dbo].[MyView]) 

Entonces sólo tiene que importar la función en lugar de la vista.

+1

¿Cómo crearías asociaciones entre las entidades que utilizan este enfoque? ¿Es posible? – ggderas

Cuestiones relacionadas