Entity Framework: tabla sin clave primaria

165

Tengo una base de datos existente con la que me gustaría construir una nueva aplicación usando EF4.0

Algunas tablas no tienen claves primarias definidas, de modo que cuando creo un nuevo modelo de datos de entidad, recibo el siguiente mensaje:

The table/view TABLE_NAME does not have a primary key defined 
and no valid primary key could be inferred. This table/view has 
been excluded. To use the entity, you will need to review your schema, 
add the correct keys, and uncomment it.

Si quiero usarlos y modificar datos, ¿debo agregar necesariamente un PK a esas tablas, o hay una solución para no tener que hacerlo?

Cris
fuente
21
Para citar a Joe Celko: si no tiene una clave principal, no es una tabla . ¿Por qué alguien crearía una tabla "regular" sin una clave primaria? ¡Solo agrega esos PK! Los necesitará, más temprano que tarde ...
marc_s
44
Si es una vista, esto ha sido una mirada a este caso stackoverflow.com/a/10302066/413032
Davut Gürbüz
50
Es perfectamente válido que no todas las tablas necesiten una clave primaria. No suele ser útil, pero es válido. Confundir EF es una buena razón, no es que tome mucho. ;-).
Suncat2000
25
Imagine que no puedo modificar la estructura de la base de datos en mi empresa y fue creada por alguien que no cambiará la estructura de la tabla, este escenario es posible.
Tito
44
Aquí es exactamente donde estamos. Tenemos que trabajar con una base de datos Oracle de terceros que no tenga claves primarias.
David Brower

Respuestas:

58

El error significa exactamente lo que dice.

Incluso si pudieras solucionar esto, confía en mí, no quieres. La cantidad de errores confusos que podrían introducirse es asombrosa y aterradora, sin mencionar el hecho de que su rendimiento probablemente se irá por los aires.

No evites esto. Arregla tu modelo de datos.

EDITAR: He visto que varias personas están rechazando esta pregunta. Eso está bien, supongo, pero tenga en cuenta que el OP preguntó sobre el mapeo de una tabla sin una clave primaria, no una vista . La respuesta sigue siendo la misma. Evitar la necesidad de EF de tener un PK en las tablas es una mala idea desde el punto de vista de la capacidad de administración, la integridad de los datos y el rendimiento.

Algunos han comentado que no tienen la capacidad de corregir el modelo de datos subyacente porque están asignando a una aplicación de terceros. Esa no es una buena idea, ya que el modelo puede cambiar por debajo de usted. Podría decirse que, en ese caso, querrá asignar a una vista, que, de nuevo, no es lo que solicitó el OP.

Dave Markle
fuente
44
Acuerde en senarios comunes pero en senarios raros como la tabla LOG solo necesita insertar registros lo antes posible. Tener PK puede ser un problema cuando se verifica la unicidad y la indexación. Además, si su PK es IDENTIDAD, entonces devolver el valor generado al EF es otro problema. usando GUID en su lugar? ¡el tiempo de generación y la indexación / clasificación es otro problema! ... ¡Así que en algunos senderos OLTP críticos (como el registro) no tener PK es un punto y no tiene ningún punto positivo!
Mahmoud Moravej
55
@MahmoudMoravej: Primero, no mezcle las ideas de agrupación de índices y claves primarias. No son lo mismo. Puede tener inserciones de muy alto rendimiento en tablas con indicios agrupados en columnas IDENTITY. Si tiene problemas con el mantenimiento del índice, debe particionar la tabla correctamente. Dejar una tabla sin índice agrupado también significa que no puede desfragmentarla de manera efectiva para recuperar espacio después de las eliminaciones. Me da lástima la pobre persona que intenta consultar su tabla de registro si no tiene índices.
Dave Markle
149
"Arreglar su modelo de datos" no es una respuesta real. A veces tenemos que vivir con situaciones menos que ideales que no creamos y que no podemos cambiar. Y, como dijo @Colin, hay una manera de hacer exactamente lo que el OP estaba pidiendo.
TheSmurf
13
Cambiar el código para satisfacer EF es una solución en sí misma. No todas las tablas necesitan una clave primaria ni deberían ser forzadas a hacerlo. Por ejemplo, tiene un tema y tiene 0 o muchas palabras clave. La tabla de palabras clave puede tener una identificación de tema principal y una palabra clave correspondiente. Decir que necesito remodelar mi DB porque EF me obliga a hacerlo es cojo.
Mrchief
14
Esto debería ser rechazado porque no responde la pregunta. A menudo necesitamos trabajar con bases de datos de terceros que no se pueden cambiar.
Kurren
104

Creo que esto lo resuelve Tillito:

Entity Framework y SQL Server View

Citaré su entrada a continuació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

respondido 26/04/10 a las 17:00 por Tillito

Colin
fuente
66
+1 Esta es la respuesta correcta, en un mundo perfecto sería genial entrar y modificar todas las bases de datos heredadas para tener integridad referencial, pero en realidad eso no siempre es posible.
Dylan Hayes
9
No recomendaría esto Particularmente la parte ISNULL. Si EF detecta dos PK iguales, es posible que no represente los registros únicos y, en su lugar, devuelva un objeto compartido. Esto me ha pasado antes.
Todd
@Todd: ¿cómo podría suceder eso si MyPrimaryID es una columna NOT NULL?
JoeCool
@JoeCool, solo porque NO ES NULO no significa que ES único. Voté "ESTA SOLUCIÓN FUNCIONA ...", porque no importa en qué contexto se use, puede estar seguro de que es único. Aunque esté pensando en ello ahora, si se elimina un registro, eso cambiará efectivamente los siguientes "PK".
Todd
1
-1, porque esto no responde cómo configurar el código de Entity Framework / C # para tratar cómo asignar a una tabla que carece de un origen de identidad. Algunos programas de terceros se escriben (por alguna razón) de esta manera.
Andrew Gray
27

Si quiero usarlos y modificar datos, ¿debo agregar necesariamente un PK a esas tablas, o hay una solución para no tener que hacerlo?

Para aquellos que llegan a esta pregunta y están utilizando Entity Framework Core, ya no es necesario agregar necesariamente una PK a esas tablas o hacer ninguna solución alternativa. Desde EF Core 2.1 tenemos una nueva característica Tipos de consulta

Los tipos de consulta deben usarse para:

  • Sirviendo como el tipo de retorno para consultas FromSql () ad hoc.
  • Asignación a vistas de bases de datos.
  • Asignación a tablas que no tienen una clave primaria definida.
  • Asignación a consultas definidas en el modelo.

Entonces, en su DbContext, simplemente agregue la siguiente propiedad de tipo en DbQuery<T>lugar de la DbSet<T>siguiente. Asumiendo que el nombre de su tabla es MyTable:

public DbQuery<MyTable> MyTables { get; set; }
CodeNotFound
fuente
1
¡La mejor respuesta si estás usando EF Core!
Jay
17

Las claves compuestas también se pueden hacer con Entity Framework Fluent API

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}
Adrián García
fuente
En este tipo de caso de 'mapeo manual', descubrí que especificar una clave personalizada como muestra es efectivo; Además, si no tiene el beneficio de una clave compuesta (como se muestra en esta respuesta) puede etiquetar una modelBuilder.Entity<T>()llamada en cadena .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)para esas claves tan especiales que no son naturales o compuestas, pero que pueden confiarse sobre ser único (por lo general, de todos modos)
Andrew Gray
5

En mi caso, tuve que asignar una entidad a una Vista, que no tenía clave primaria. Además, no se me permitió modificar esta Vista. Afortunadamente, esta vista tenía una columna que era una cadena única. Mi solución fue marcar esta columna como clave principal:

[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[StringLength(255)]
public string UserSID { get; set; }

Engañado EF. Funcionó perfectamente, nadie se dio cuenta ... :)

Boris Lipschitz
fuente
no .. ¡Creará la UserIDcolumna si usa el enfoque Code First ..!
Deepak Sharma
1
No has engañado a EF. Acabas de ordenar que apagues la Itentityfunción. Básicamente, si estoy en lo cierto, seguirá creando una UserIDcolumna para usted como PK, pero no aumentará automáticamente UserIDcuando cree un nuevo registro como lo haría de forma predeterminada. Además, aún debe mantener valores distintos UserID.
Celdor
1
@Celdor UserSID es una cadena, nunca se "aumentará automáticamente". Si se tratara de una columna de identidad entera, la base de datos la incrementaría al insertarla, no en Entity Framework.
reggaeguitar
4

EF no requiere una clave primaria en la base de datos. Si lo hiciera, no podría vincular entidades a las vistas.

Puede modificar el SSDL (y el CSDL) para especificar un campo único como su clave principal. Si no tiene un campo único, entonces creo que tiene una manguera. Pero realmente debería tener un campo único (y un PK), de lo contrario, tendrá problemas más adelante.

Erick

Erick T
fuente
Esto evita el hack ISNULL. Pero dependiendo de la situación, se pueden requerir otras respuestas: tengo la sensación de que algunos tipos de datos no son compatibles con un PK en EF, por ejemplo.
Todd
3

Tener una clave de identidad inútil es inútil a veces. Encuentro si la identificación no se usa, ¿por qué agregarla? Sin embargo, la entidad no es tan indulgente con eso, por lo que sería mejor agregar un campo ID. Incluso en el caso de que no se use, es mejor que lidiar con los errores incesantes de la entidad sobre la clave de identidad faltante.

IyaTaisho
fuente
3

ESTA SOLUCIÓN FUNCIONA

No necesita mapear manualmente, incluso si no tiene una PK. Solo necesita decirle al EF que una de sus columnas es index y la columna index no es anulable.

Para hacer esto, puede agregar un número de fila a su vista con la función isNull como la siguiente

select 
    ISNULL(ROW_NUMBER() OVER (ORDER BY xxx), - 9999) AS id
from a

ISNULL(id, number) es el punto clave aquí porque le dice al EF que esta columna puede ser la clave principal

Barny
fuente
2
Sin embargo, no sugeriría la parte ISNULL. Si EF detecta dos PK iguales, es posible que no represente el registro único y, en su lugar, devuelva un objeto compartido. Esto me ha pasado antes.
Todd
1
Tiene que usar isnull, de lo contrario EF no creerá que no es anulable.
Archlight
2

Las respuestas anteriores son correctas si realmente no tiene una PK.

Pero si hay uno pero simplemente no está especificado con un índice en la base de datos, y no puede cambiar la base de datos (sí, trabajo en el mundo de Dilbert), puede asignar manualmente los campos para que sean la clave.

Villano de Poker
fuente
2

Esta es solo una adición a la respuesta de @Erick T. Si no hay una sola columna con valores únicos, la solución consiste en utilizar una clave compuesta, como se indica a continuación:

[Key]
[Column("LAST_NAME", Order = 1)]
public string LastName { get; set; }

[Key]
[Column("FIRST_NAME", Order = 2)]
public string FirstName { get; set; }

De nuevo, esto es solo una solución. La solución real es arreglar el modelo de datos.

Desarrollador
fuente
2

Esto quizás tarde para responder ... sin embargo ...

Si una tabla no tiene una clave primaria, entonces hay algunos escenarios que deben analizarse para que el EF funcione correctamente. La regla es: EF trabajará con tablas / clases con clave primaria. Así es como se hace el seguimiento ...

Digamos, su tabla 1. Los registros son únicos: la unicidad está hecha por una sola columna de clave externa: 2. Los registros son únicos: la unicidad está hecha por una combinación de múltiples columnas. 3. Los registros no son únicos (en su mayor parte *).

Para los escenarios n. ° 1 y n. ° 2, puede agregar la siguiente línea al método OnModelCreating del módulo DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // tantas columnas como sean necesarias para que los registros sean únicos.

Para el escenario # 3, aún puede usar la solución anterior (# 1 + # 2) después de estudiar la tabla (* lo que hace que todos los registros sean únicos de todos modos). Si debe incluir TODAS las columnas para que todos los registros sean únicos, puede agregar una columna de clave principal a su tabla. Si esta tabla es de un proveedor externo, clone esta tabla en su base de datos local (durante la noche o la cantidad de tiempo que necesite) con la columna de clave principal agregada arbitrariamente a través de su secuencia de comandos de clonación.

Sam Saarian
fuente
1
  1. Cambie la estructura de la tabla y agregue una columna primaria. Actualiza el modelo
  2. Modifique el archivo .EDMX en el Editor XML e intente agregar una Nueva columna debajo de la etiqueta para esta tabla específica (NO FUNCIONARÁ)
  3. En lugar de crear una nueva tabla Columna primaria a salida, crearé una clave compuesta involucrando todas las columnas existentes ( TRABAJADO )

Entity Framework: Agregar DataTable sin clave primaria al modelo de entidad.

Pratap
fuente
Probé el enfoque de clave compuesta con EF 4.0 y no funcionó.
Ralph Willgoss
Este enfoque funcionó perfectamente para mí, puede ser una molestia trabajar con sistemas heredados "a veces" ...
pinmonkeyiii
1

Actualice a la respuesta de @CodeNotFound .

En EF Core 3.0 DbQuery<T>ha quedado en desuso, en su lugar, debe usar tipos de entidad sin clave que supuestamente hacen lo mismo. Estos se configuran con el HasNoKey()método ModelBuilder . En su clase DbContext, haga esto

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder
        .Entity<YourEntityType>(eb =>
        {
            eb.HasNoKey();
        });

}

Sin embargo, existen restricciones, en particular:

  • Nunca se realiza un seguimiento de los cambios en el DbContext y, por lo tanto, nunca se insertan, actualizan o eliminan en la base de datos.
  • Solo admite un subconjunto de capacidades de mapeo de navegación, específicamente:
    • Puede que nunca actúen como el fin principal de una relación.
    • Es posible que no tengan navegaciones a entidades propias
    • Solo pueden contener propiedades de navegación de referencia que apuntan a entidades regulares.
    • Las entidades no pueden contener propiedades de navegación para tipos de entidad sin clave.

Esto significa que para la cuestión de

Si quiero usarlos y modificar datos, ¿debo agregar necesariamente un PK a esas tablas, o hay una solución para no tener que hacerlo?

No puede modificar los datos de esta manera; sin embargo, puede leer. Sin embargo, podría imaginarse usar otra forma (por ejemplo, ADO.NET, Dapper) para modificar datos; esta podría ser una solución en casos en los que rara vez necesita realizar operaciones que no sean de lectura y todavía le gustaría seguir con EF Core para sus casos mayoritarios.

Además, si realmente necesita / desea trabajar con tablas de montón (sin clave), considere deshacerse de EF y use otra forma de hablar con su base de datos.

Peter Lindsten
fuente
0

Desde un punto de vista práctico, cada mesa, incluso una mesa desnormalizada como una mesa de almacén, debe tener una clave primaria. O, en su defecto, al menos debería tener un índice único no anulable.

Sin algún tipo de clave única, los registros duplicados pueden (y aparecerán) en la tabla, lo cual es muy problemático tanto para las capas ORM como para la comprensión básica de los datos. Una tabla que tiene registros duplicados es probablemente un síntoma de mal diseño.

Como mínimo, la tabla debe tener al menos una columna de identidad. Agregar una columna de ID de generación automática tarda aproximadamente 2 minutos en SQL Server y 5 minutos en Oracle. Para ese esfuerzo extra, se evitarán muchos, muchos problemas.

Ash8087
fuente
Mi aplicación está en una configuración de almacén de datos (con Oracle) y me convenciste de pasar por los 5 minutos para agregar un índice. Realmente solo lleva 5 minutos (o un poco más si necesita buscarlo o modificar ETL).
Trent
0

También encontramos este problema, y ​​aunque teníamos una columna que tenía nulos, lo importante era que teníamos una columna dependiente que no tenía nulos y que la combinación de estas dos columnas era única.

Entonces, para citar la respuesta dada por Pratap Reddy, funcionó bien para nosotros.

Azerax
fuente
-8

La tabla solo necesita tener una columna que no permita valores nulos

señor9
fuente