Entity Framework: table sans clé primaire

165

J'ai une base de données existante avec laquelle je voudrais créer une nouvelle application en utilisant EF4.0

Certaines tables n'ont pas de clés primaires définies, de sorte que lorsque je crée un nouveau modèle de données d'entité, j'obtiens le message suivant:

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 je veux les utiliser et modifier les données, dois-je nécessairement ajouter un PK à ces tables, ou existe-t-il une solution de contournement pour ne pas avoir à le faire?

Cris
la source
21
Pour citer Joe Celko: s'il n'a pas de clé primaire, ce n'est pas une table . Pourquoi diable quelqu'un créerait-il une table "ordinaire" sans clé primaire? Ajoutez simplement ces PK! Vous en aurez besoin - plutôt tôt que tard ....
marc_s
4
Si c'est une vue cette hava un coup d'oeil cette affaire stackoverflow.com/a/10302066/413032
Davut Gürbüz
50
Il est parfaitement valide que toutes les tables n'ont pas besoin d'une clé primaire. Pas souvent utile, mais valable. Confondre EF est une bonne raison, pas qu'il en faut beaucoup. ;-).
Suncat2000
25
Imaginez que je ne puisse pas modifier la structure de la base de données de mon entreprise et qu'elle a été créée par quelqu'un qui ne changera pas la structure de la table, ce scénario est possible.
Tito
4
C'est exactement là où nous en sommes. Nous devons travailler avec une base de données Oracle tierce qui n'a pas de clés primaires.
David Brower

Réponses:

58

L'erreur signifie exactement ce qu'elle dit.

Même si vous pouviez contourner ce problème, croyez-moi, vous ne le voulez pas. Le nombre de bugs déroutants qui pourraient être introduits est stupéfiant et effrayant, sans parler du fait que votre performance passera probablement dans les tubes.

Ne contournez pas ça. Corrigez votre modèle de données.

EDIT: J'ai vu qu'un certain nombre de personnes votent contre cette question. C'est bien, je suppose, mais gardez à l'esprit que l'OP a demandé à mapper une table sans clé primaire, pas une vue . La réponse est toujours la même. Contourner le besoin de l'EF d'avoir une PK sur les tables est une mauvaise idée du point de vue de la gérabilité, de l'intégrité des données et des performances.

Certains ont indiqué qu'ils n'avaient pas la possibilité de réparer le modèle de données sous-jacent car ils étaient mappés à une application tierce. Ce n'est pas une bonne idée, car le modèle peut changer sous vous. On peut soutenir que dans ce cas, vous voudriez mapper sur une vue, ce qui, encore une fois, n'est pas ce que le PO a demandé.

Dave Markle
la source
44
Acceptez les scénarios courants, mais dans de rares scénarios tels que la table LOG, il vous suffit d'insérer des enregistrements dès que possible. Avoir PK peut être un problème lors de la vérification de l'unicité et de l'indexation. De plus, si votre PK est IDENTITY, renvoyer la valeur générée à l'EF est un autre problème. en utilisant GUID à la place? le temps de génération et l'indexation / tri est un autre problème! ... DONC DANS certains senarios OLTP critiques (comme Logging) ne pas avoir de PK est un point et l'avoir n'a aucun point positif!
Mahmoud Moravej
5
@MahmoudMoravej: Tout d'abord, ne mélangez pas les idées d'index de clustering et de clés primaires. Ce n'est pas la même chose. Vous pouvez avoir des insertions très performantes sur des tables avec des indices groupés sur des colonnes IDENTITY. Si vous rencontrez des problèmes avec la maintenance de l'index, vous devez partitionner la table correctement. Laisser une table sans index clusterisé signifie également que vous ne pouvez pas la défragmenter efficacement pour récupérer de l'espace après les suppressions. Je plains la pauvre personne qui essaie d'interroger votre table de journalisation si elle n'a pas d'index.
Dave Markle
149
«Réparez votre modèle de données» n'est pas une vraie réponse. Parfois, nous devons vivre avec des situations moins qu'idéales que nous n'avons pas créées et que nous ne pouvons pas changer. Et, comme @Colin l'a dit, il existe un moyen de faire exactement ce que le PO demandait.
TheSmurf
13
La modification du code pour satisfaire EF est une solution de contournement en soi. Toutes les tables n'ont pas besoin d'une clé primaire et ne devraient pas y être forcées. Par exemple, vous avez un sujet et il a 0 ou plusieurs mots-clés. La table des mots-clés peut avoir un identifiant de rubrique parent et un mot-clé correspondant. Dire que j'ai besoin de remodeler ma DB parce que EF m'y oblige est nul.
Mrchief
14
Cela devrait être rejeté car il ne répond pas à la question. Nous devons souvent travailler avec des bases de données tierces qui ne peuvent pas être modifiées.
Kurren
104

Je pense que cela est résolu par Tillito:

Entity Framework et vue SQL Server

Je vais citer son entrée ci-dessous:

Nous avons eu le même problème et voici la solution:

Pour forcer la structure d'entité à utiliser une colonne comme clé primaire, utilisez ISNULL.

Pour forcer la structure d'entité à ne pas utiliser une colonne comme clé primaire, utilisez NULLIF.

Un moyen simple d'appliquer ceci est d'encapsuler l'instruction select de votre vue dans un autre select.

Exemple:

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

26 avr. 10 à 17:00 par Tillito

Colin
la source
6
+1 C'est la bonne réponse, dans un monde parfait, ce serait formidable d'entrer et de modifier toutes les bases de données héritées pour avoir une intégrité référentielle, mais en réalité ce n'est pas toujours possible.
Dylan Hayes
9
Je ne recommanderais pas ça. En particulier la partie ISNULL. Si EF détecte deux PK identiques, il se peut qu'il ne rende pas les enregistrements uniques et renvoie à la place un objet partagé. ça m'est déja arrivé.
Todd
@Todd - comment cela pourrait-il arriver si MyPrimaryID est une colonne NOT NULL?
JoeCool
@JoeCool, ce n'est pas parce que ce n'est PAS NULL qu'il est unique. J'ai voté pour "CETTE SOLUTION FONCTIONNE ...", car quel que soit le contexte dans lequel elle est utilisée, vous pouvez être assuré de l'unicité. Bien qu'en y réfléchissant maintenant, si un enregistrement est supprimé, cela changera effectivement le "PK" des enregistrements suivants.
Todd du
1
-1, car cela ne répond pas à la configuration du code Entity Framework / C # pour gérer le mappage à une table qui n'a pas de valeur de départ d'identité. Certains logiciels tiers sont (pour une raison quelconque) écrits de cette façon.
Andrew Gray
27

Si je veux les utiliser et modifier les données, dois-je nécessairement ajouter un PK à ces tables, ou existe-t-il une solution de contournement pour ne pas avoir à le faire?

Pour ceux qui atteignent cette question et utilisent Entity Framework Core, vous n'avez plus nécessairement besoin d'ajouter un PK à ces tables ou de faire une solution de contournement. Depuis EF Core 2.1, nous avons une nouvelle fonctionnalité Types de requêtes

Les types de requête doivent être utilisés pour:

  • Servant de type de retour pour les requêtes FromSql () ad hoc.
  • Mappage aux vues de base de données.
  • Mappage vers des tables pour lesquelles aucune clé primaire n'est définie.
  • Mappage aux requêtes définies dans le modèle.

Donc, dans votre DbContext, ajoutez simplement la propriété suivante de type DbQuery<T>au lieu de DbSet<T>celle ci-dessous. En supposant que le nom de votre table est MyTable:

public DbQuery<MyTable> MyTables { get; set; }
CodeNotFound
la source
1
Meilleure réponse si vous utilisez EF Core!
Jay
17

Les clés composites peuvent également être créées avec l'API Entity Framework Fluent

public class MyModelConfiguration : EntityTypeConfiguration<MyModel>
{
     public MyModelConfiguration()
     {
        ToTable("MY_MODEL_TABLE");
        HasKey(x => new { x.SourceId, x.StartDate, x.EndDate, x.GmsDate });
        ...
     }
}
Adrian Garcia
la source
Dans ce genre de cas de «mappage manuel», j'ai trouvé que la spécification d'une clé personnalisée lorsque vous montrez est efficace; de plus, si vous ne bénéficiez pas d'une clé composite (comme indiqué dans cette réponse), vous pouvez marquer un modelBuilder.Entity<T>()appel en chaîne avec .HasDatabaseGeneratedOption(DatabaseGeneratedOption.None)pour ces clés si spéciales qui ne sont pas naturelles ou composites, mais qui peuvent être fiables être unique (généralement, de toute façon.)
Andrew Gray
5

Dans mon cas, j'ai dû mapper une entité à une vue, qui n'avait pas de clé primaire. De plus, je n'étais pas autorisé à modifier cette vue. Heureusement, cette vue avait une colonne qui était une chaîne unique. Ma solution était de marquer cette colonne comme clé primaire:

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

EF trompé. A parfaitement fonctionné, personne n'a remarqué ... :)

Boris Lipschitz
la source
non .. Il créera la UserIDcolonne si vous utilisez l'approche Code First ..!
Deepak Sharma
1
Vous n'avez pas trompé EF. Vous venez de commander de désactiver la Itentityfonction. Fondamentalement, si j'ai raison, cela va toujours créer une UserIDcolonne pour vous en tant que PK, mais cela n'augmentera pas automatiquement UserIDlorsque vous créez un nouvel enregistrement comme le serait par défaut. En outre, vous devez toujours conserver des valeurs distinctes dans UserID.
Celdor
1
@Celdor UserSID est une chaîne, elle ne serait jamais "augmentée automatiquement". S'il s'agissait d'une colonne d'identité entière, la base de données l'incrémenterait lors de l'insertion, pas Entity Framework.
reggaeguitar
4

EF ne nécessite pas de clé primaire sur la base de données. Si tel était le cas, vous ne pouviez pas lier les entités aux vues.

Vous pouvez modifier le SSDL (et le CSDL) pour spécifier un champ unique comme clé primaire. Si vous n'avez pas de champ unique, alors je crois que vous êtes arrosé. Mais vous devriez vraiment avoir un champ unique (et un PK), sinon vous allez rencontrer des problèmes plus tard.

Erick

Erick T
la source
Cela évite le hack ISNULL. Mais selon la situation, d'autres réponses peuvent être nécessaires - j'ai le sentiment que certains types de données ne sont pas pris en charge pour un PK dans EF par exemple.
Todd
3

Avoir une clé d'identité inutile est parfois inutile. Je trouve que si l'identifiant n'est pas utilisé, pourquoi l'ajouter? Cependant, Entity ne pardonne pas trop, donc ajouter un champ ID serait préférable. Même dans le cas où il n'est pas utilisé, c'est mieux que de traiter les erreurs incessantes d'Entity concernant la clé d'identité manquante.

IyaTaisho
la source
3

CETTE SOLUTION FONCTIONNE

Vous n'avez pas besoin de mapper manuellement même si vous n'avez pas de PK. Il vous suffit d'indiquer à l'EF que l'une de vos colonnes est index et que la colonne d'index n'est pas nullable.

Pour ce faire, vous pouvez ajouter un numéro de ligne à votre vue avec la fonction isNull comme suit

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

ISNULL(id, number) est le point clé ici car il indique à l'EF que cette colonne peut être la clé primaire

Barny
la source
2
Je ne suggérerais pas la partie ISNULL cependant. Si EF détecte deux PK identiques, il se peut qu'il ne rende pas l'enregistrement unique et renvoie à la place un objet partagé. ça m'est déja arrivé.
Todd
1
Vous devez utiliser isnull, sinon EF ne croira pas qu'il n'est pas nullable.
Archlight
2

Les réponses ci-dessus sont correctes si vous n'avez vraiment pas de PK.

Mais s'il y en a un mais qu'il n'est tout simplement pas spécifié avec un index dans la base de données, et que vous ne pouvez pas changer la base de données (oui, je travaille dans le monde de Dilbert), vous pouvez mapper manuellement le (s) champ (s) pour être la clé.

Poker méchant
la source
2

Ceci est juste un ajout à la réponse de @Erick T. S'il n'y a pas de colonne unique avec des valeurs uniques, la solution de contournement consiste à utiliser une clé composite, comme suit:

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

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

Encore une fois, ce n'est qu'une solution de contournement. La vraie solution est de corriger le modèle de données.

Développeur
la source
2

C'est peut-être trop tard pour répondre ... cependant ...

Si une table n'a pas de clé primaire, il existe peu de scénarios qui doivent être analysés pour que l'EF fonctionne correctement. La règle est la suivante: EF fonctionnera avec des tables / classes avec une clé primaire. C'est ainsi qu'il effectue le suivi ...

Disons, votre table 1. Les enregistrements sont uniques: l'unicité est faite par une seule colonne de clé étrangère: 2. Les enregistrements sont uniques: l'unicité est faite par une combinaison de plusieurs colonnes. 3. Les enregistrements ne sont pas uniques (pour la plupart *).

Pour les scénarios n ° 1 et n ° 2, vous pouvez ajouter la ligne suivante à la méthode OnModelCreating du module DbContext: modelBuilder.Entity (). HasKey (x => new {x.column_a, x.column_b}); // autant de colonnes que nécessaire pour rendre les enregistrements uniques.

Pour le scénario n ° 3, vous pouvez toujours utiliser la solution ci-dessus (n ° 1 + n ° 2) après avoir étudié la table (* ce qui rend tous les enregistrements uniques de toute façon). Si vous devez inclure TOUTES les colonnes pour rendre tous les enregistrements uniques, vous pouvez ajouter une colonne de clé primaire à votre table. Si cette table provient d'un fournisseur tiers, clonez cette table dans votre base de données locale (pendant la nuit ou autant de fois que vous en avez besoin) avec la colonne de clé primaire ajoutée de manière arbitraire via votre script de clonage.

Sam Saarian
la source
1
  1. Modifiez la structure du tableau et ajoutez une colonne principale. Mettre à jour le modèle
  2. Modifiez le fichier .EDMX dans l'éditeur XML et essayez d'ajouter une nouvelle colonne sous la balise pour cette table spécifique (NE FONCTIONNE PAS)
  3. Au lieu de créer une nouvelle colonne principale vers la table de sortie, je vais créer une clé composite en impliquant toutes les colonnes existantes ( WORKED )

Entity Framework: ajout de DataTable sans clé primaire au modèle d'entité.

Pratap
la source
J'ai essayé l'approche de clé composite avec EF 4.0 et cela n'a pas fonctionné.
Ralph Willgoss
Cette approche a parfaitement fonctionné pour moi, peut être
pénible de
1

Mettez à jour la réponse de @CodeNotFound .

Dans EF Core 3.0 DbQuery<T>est obsolète, vous devez à la place utiliser des types d'entité Keyless qui font supposément la même chose. Ceux-ci sont configurés avec la HasNoKey()méthode ModelBuilder . Dans votre classe DbContext, faites ceci

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

}

Il existe cependant des restrictions, notamment:

  • Ne sont jamais suivis pour les modifications dans le DbContext et ne sont donc jamais insérés, mis à jour ou supprimés dans la base de données.
  • Prend en charge uniquement un sous-ensemble de fonctionnalités de cartographie de navigation, en particulier:
    • Ils ne peuvent jamais agir comme la principale fin d'une relation.
    • Ils peuvent ne pas avoir de navigation vers les entités détenues
    • Ils ne peuvent contenir que des propriétés de navigation de référence pointant vers des entités régulières.
    • Les entités ne peuvent pas contenir de propriétés de navigation vers les types d'entités sans clé.

Cela signifie que pour la question de

Si je veux les utiliser et modifier les données, dois-je nécessairement ajouter un PK à ces tables, ou existe-t-il une solution de contournement pour ne pas avoir à le faire?

Vous ne pouvez pas modifier les données de cette façon - cependant vous pouvez lire. On pourrait cependant envisager d'utiliser une autre méthode (par exemple ADO.NET, Dapper) pour modifier les données - cela pourrait être une solution dans les cas où vous avez rarement besoin de faire des opérations sans lecture et que vous souhaitez toujours vous en tenir à EF Core dans vos cas majoritaires.

De plus, si vous avez vraiment besoin / souhaitez travailler avec des tables de tas (sans clé), envisagez d'abandonner EF et utilisez une autre façon de communiquer avec votre base de données.

Peter Lindsten
la source
0

D'un point de vue pratique, chaque table - même une table dénormalisée comme une table d'entrepôt - doit avoir une clé primaire. Ou, à défaut, il devrait au moins avoir un index unique, non nullable.

Sans une sorte de clé unique, des enregistrements en double peuvent (et apparaîtront) apparaître dans le tableau, ce qui est très problématique à la fois pour les couches ORM et aussi pour la compréhension de base des données. Une table contenant des enregistrements en double est probablement le symptôme d'une mauvaise conception.

À tout le moins, la table doit au moins avoir une colonne d'identité. L'ajout d'une colonne d'ID à génération automatique prend environ 2 minutes dans SQL Server et 5 minutes dans Oracle. Pour cet effort supplémentaire, de nombreux problèmes seront évités.

Ash8087
la source
Mon application est dans un environnement d'entrepôt de données (avec Oracle) et vous m'avez convaincu de passer les 5 minutes pour ajouter un index. Cela ne prend vraiment que 5 minutes (ou un peu plus si vous avez besoin de le rechercher ou de modifier les ETL).
Trent
0

Nous avons également rencontré ce problème et, bien que nous ayons une colonne contenant des valeurs nulles, ce qui était important, c'était que nous ayons une colonne dépendante qui n'avait pas de valeurs nulles et que la combinaison de ces deux colonnes était unique.

Donc, pour citer la réponse de Pratap Reddy, cela a bien fonctionné pour nous.

Azerax
la source
0

Je suis très heureux que mon problème soit résolu.

Impossible de mettre à jour EntitySet - car il a un DefiningQuery et aucun élément <UpdateFunction> n'existe

et faites ceci: Regardez en dessous de cette ligne et trouvez l'étiquette. Il contiendra une grande instruction select. Supprimez la balise et son contenu.

maintenant sans changer DB TI peut insérer dans une table qui n'a pas PK

merci à tous et merci Pharylon

aseman arabsorkhi
la source
-8

La table doit juste avoir une colonne qui n'autorise pas les valeurs nulles

monsieur9
la source