Pregunta

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 creará una entidad basada en esta consulta, lo que hace, pero la 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 infirió y la definición se creó como una tabla / vista de solo lectura.

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

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

¿Fue útil?

Solución

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

Para obligar al marco de la entidad a usar una columna como clave principal, use ISNULL.

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

Una manera fácil de aplicar esto es envolver la declaración select de su vista en otra selección.

Ejemplo:

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

Otros consejos

Pude resolver esto 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 " Clave de entidad " está marcado.
  4. Multiseleccione 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 " Clave de entidad " desplegable 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 desaparecieran.

No tuve que cambiar mi vista para usar las soluciones 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.

De acuerdo con @Tillito, sin embargo, en la mayoría de los casos dañará el optimizador de SQL y no usará los índices correctos.

Puede ser obvio para alguien, pero me quedé horas resolviendo problemas de rendimiento con la solución Tillito. Digamos que tienes 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 así

 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á index ix_customer y realizará un escaneo de tabla en el índice primario, pero si en lugar de:

Group by CustomerId

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í. Uso ISNULL () para el campo de clave principal y COALESCE () si el campo no debe ser la clave principal, sino que también debe tener un valor no anulable. Este ejemplo produce un campo de ID con una clave primaria no anulable. Los otros campos no son claves y tienen (Ninguno) como su atributo Anulable.

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

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

El generador EDM de Entity Framework actual creará una clave compuesta de todos los campos no anulables en su vista. Para obtener el control sobre esto, deberá modificar la vista y las columnas de la tabla subyacente configurando las columnas como anulables cuando no desee que formen parte de la clave principal. Lo contrario también es cierto, como encontré, la clave generada por EDM estaba causando problemas de duplicación de datos, por lo que tuve que definir una columna anulable como no anulable para forzar que la clave compuesta en el EDM incluya esa columna.

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

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

También recomiendo si no desea meterse con lo que debería ser la clave principal para incorporar ROW_NUMBER a su selección y establecerla como clave principal y establecer todas las demás columnas / miembros como no primarios en el modelo.

Debido a los problemas mencionados anteriormente, prefiero las funciones de valor 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])

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

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top