Entity Framework y SQL Server View

Por varias razones de las que no tengo libertad para hablar, estamos definiendo una vista en nuestra base de datos Sql Server 2005 de la siguiente manera:

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 Entity Framework cree una entidad basada en esta consulta, que lo hace, pero la genera con un error que dice 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 usando 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?

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

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

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

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

Ejemplo:

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

Pude resolver esto usando el diseñador.

  1. Abra el navegador de modelos.
  2. Encuentra la vista en el diagtwig.
  3. Haga clic con el botón derecho en la clave principal y asegúrese de que esté marcada la “Clave de la entidad”.
  4. Multi-seleccionar todas las claves no primarias. Use las teclas Ctrl o Shift.
  5. En la ventana Propiedades (presione F4 si es necesario para verlo), cambie el menú desplegable “Clave de la entidad” a Falso.
  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 usar 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.

Esté de acuerdo con @Tillito, sin embargo, en la mayoría de los casos, afectará al optimizador SQL y no usará índices correctos.

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

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

y tu punto de 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 

El optimizador Sql no usará el índice ix_customer y realizará el escaneo de tabla en el índice primario, pero si en lugar de:

 Group by CustomerId 

tu usas

 Group by IsNull(CustomerId, -1) 

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

Si

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, puede falsificar una utilizando ROW_NUMBER para generar una pseudo-clave que su código ignora. Por ejemplo:

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

El generador actual de EDM de Entity Framework creará una clave compuesta de todos los campos que no admiten nulos en su 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.

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 nulos. Esto funcionó para hacer que el EF pensara que solo había una clave primaria en la vista.

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

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 establecerlo como clave principal y configurar todas las demás columnas / miembros como no primarios en el modelo.

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

Si tienes esto:

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

crea esto:

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

Entonces simplemente importa la función en lugar de la vista.