171 votes

EF Code First "Nom de colonne non valide 'Discriminator'" mais pas d'héritage

J'ai une table dans ma base de données appelée SEntries (voir ci-dessous l'instruction CREATE TABLE). Elle a une clé primaire, quelques clés étrangères et rien de spécial à ce sujet. J'ai beaucoup de tables dans ma base de données similaires à celle-ci, mais pour une raison quelconque, cette table s'est retrouvée avec une colonne "Discriminator" sur la classe Proxy EF.

Voici comment la classe est déclarée en C#:

public class SEntry
{
    public long SEntryId { get; set; }

    public long OriginatorId { get; set; }
    public DateTime DatePosted { get; set; }
    public string Message { get; set; }
    public byte DataEntrySource { get; set; }
    public string SourceLink { get; set; }
    public int SourceAppId { get; set; }
    public int? LocationId { get; set; }
    public long? ActivityId { get; set; }
    public short OriginatorObjectTypeId { get; set; }
}

public class EMData : DbContext
{
    public DbSet SEntries { get; set; }
            ...
    }

Lorsque j'essaie d'ajouter une nouvelle ligne à cette table, j'obtiens l'erreur:

System.Data.SqlClient.SqlException: Nom de colonne non valide 'Discriminator'.

Ce problème ne se produit que si vous héritez de votre classe C# d'une autre classe, mais SEntry n'hérite de rien (comme vous pouvez le voir ci-dessus).

En plus de cela, une fois que j'obtiens l'info-bulle sur le débogueur lorsque je survole l'instance EMData pour la propriété SEntries, il affiche:

base {System.Data.Entity.Infrastructure.DbQuery} = {SELECT 
[Extent1].[Discriminator] AS [Discriminator], 
[Extent1].[SEntryId] AS [SEntryId], 
[Extent1].[OriginatorId] AS [OriginatorId], 
[Extent1].[DatePosted] AS [DatePosted], 
[Extent1].[Message] AS [Message], 
[Extent1].[DataEntrySource] AS [DataE...

Des suggestions ou des idées pour résoudre ce problème? J'ai essayé de renommer la table, la clé primaire et quelques autres choses, mais rien ne fonctionne.

Table SQL:

CRÉER TABLE [dbo].[SEntries](
[SEntryId] [bigint] IDENTITY(1125899906842624,1) NOT NULL,
[OriginatorId] [bigint] NOT NULL,
[DatePosted] [datetime] NOT NULL,
[Message] [nvarchar](500) NOT NULL,
[DataEntrySource] [tinyint] NOT NULL,
[SourceLink] [nvarchar](100) NULL,
[SourceAppId] [int] NOT NULL,
[LocationId] [int] NULL,
[ActivityId] [bigint] NULL,
[OriginatorObjectTypeId] [smallint] NOT NULL,
CONSTRAINT [PK_SEntries] PRIMARY KEY CLUSTERED 
(
[SEntryId] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF,       ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[SEntries]  WITH CHECK ADD  CONSTRAINT [FK_SEntries_ObjectTypes] FOREIGN KEY([OriginatorObjectTypeId])
REFERENCES [dbo].[ObjectTypes] ([ObjectTypeId])
GO

ALTER TABLE [dbo].[SEntries] CHECK CONSTRAINT [FK_SEntries_ObjectTypes]
GO

ALTER TABLE [dbo].[SEntries]  WITH CHECK ADD  CONSTRAINT [FK_SEntries_SourceApps] FOREIGN KEY([SourceAppId])
REFERENCES [dbo].[SourceApps] ([SourceAppId])
GO

ALTER TABLE [dbo].[SEntries] CHECK CONSTRAINT [FK_SEntries_SourceApps]
GO

18 votes

Pour la personne suivante qui passera du temps à essayer de comprendre cela, ce qui s'est passé, c'est qu'à un autre endroit dans le code, j'avais une classe qui héritait de SEntry, bien que ce ne soit pas une classe qui serait jamais stockée dans la base de données. Donc tout ce que je devais faire était d'ajouter [NotMapped] en tant qu'attribut de cette classe !

1 votes

Je reçois cette erreur si je ne mets pas [NotMapped] sur la classe ApplicationUser dans Identitymodel.cs

0 votes

J'ai un problème similaire, posté en détail sur stackoverflow.com/questions/75333465/…

355voto

Marcelo Calbucci Points 1180

Il s'avère que Entity Framework supposera que toute classe qui hérite d'une classe POCO qui est mappée à une table sur la base de données nécessite une colonne Discriminator, même si la classe dérivée ne sera pas enregistrée dans la base de données.

La solution est assez simple et il vous suffit d'ajouter [NotMapped] en tant qu'attribut de la classe dérivée.

Exemple :

class Person
{
    public string Name { get; set; }
}

[NotMapped]
class PersonViewModel : Person
{
    public bool UpdateProfile { get; set; }
}

Maintenant, même si vous mappez la classe Person à la table Person dans la base de données, une colonne "Discriminator" ne sera pas créée car la classe dérivée a [NotMapped].

En tant que conseil supplémentaire, vous pouvez utiliser [NotMapped] pour les propriétés que vous ne voulez pas mapper à un champ dans la base de données.

7 votes

Ok donc voilà 3 heures de ma vie ;( mais merci beaucoup quand même. Je devrais aussi ajouter pour être clair... les classes dérivées peuvent être tout à fait à l'écart et en aucun cas utilisées concernant la persistance et EF tentera quand même de les dessiner... très déroutant.

13 votes

Si vous ne trouvez pas [NotMapped] veuillez ajouter une référence à : "System.ComponentModel.DataAnnotations" au projet à partir de "Assembly Framework".

10 votes

Utilisation de System.ComponentModel.DataAnnotations.Schema;

47voto

Walter Stabosz Points 3012

Voici la syntaxe de l'API fluide.

http://blogs.msdn.com/b/adonet/archive/2010/12/06/ef-feature-ctp5-fluent-api-samples.aspx

class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string FullName { 
        get {
            return this.FirstName + " " + this.LastName;
        }
    }
}

class PersonViewModel : Person
{
    public bool UpdateProfile { get; set; }
}

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    // ignorer un type qui n'est pas mappé à une table de base de données
    modelBuilder.Ignore();

    // ignorer une propriété qui n'est pas mappée à une colonne de base de données
    modelBuilder.Entity()
        .Ignore(p => p.FullName);

}

0 votes

Ne serait-il pas plus judicieux d'ajouter simplement l'attribut [NotMapped] ?

2 votes

@Keith ma réponse est comment ignorer une colonne en utilisant l'API fluide, qui n'utilise pas d'attributs tels que [NotMapped]

1 votes

Keith c'est la réponse préférée je pense parce que nous nous orientons désormais vers une norme de code en premier et la réponse de Walter est mieux adaptée à ce scénario, surtout si vous finissez par utiliser des migrations de base de données.

9voto

Seph Points 4047

Je viens de rencontrer ceci et mon problème était causé par le fait d'avoir deux entités toutes deux avec l'attribut System.ComponentModel.DataAnnotations.Schema.TableAttribute se référant à la même table.

par exemple:

[Table("foo")]
public class foo
{
    // du contenu ici
}

[Table("foo")]
public class fooExtended
{
    // encore plus de contenu ici
}

changer le deuxième de foo à foo_extended a résolu ce problème pour moi et j'utilise maintenant la Table par Type (TPT)

0 votes

Cela n'a pas fonctionné pour moi : Les types d'entités 'AtencionMedica' et 'AtencionMedicaAP' ne peuvent pas partager la table 'AtencionMedicas' car ils ne sont pas dans la même hiérarchie de type

0 votes

Merci, cela m'a aidé, j'ai eu le même problème en utilisant l'API fluide: var entity = modelBuilder.Entity().ToTable("ENTITY_TABLE"), et ensuite une autre ligne en utilisant soit le même EntityObject soit le même ENTITY_TABLE.

8voto

KwakuCsc Points 49

J'avais un problème similaire, pas exactement les mêmes conditions, et ensuite j'ai vu cet article. J'espère que cela aidera quelqu'un. Apparemment, j'utilisais l'un de mes modèles d'entité EF comme classe de base pour un type qui n'était pas spécifié comme un db set dans mon dbcontext. Pour résoudre ce problème, j'ai dû créer une classe de base qui avait toutes les propriétés communes aux deux types et hériter de la nouvelle classe de base parmi les deux types.

Exemple:

//Mauvais modèle
    //classe définie dans dbcontext comme un dbset
    public class Customer{ 
       public int Id {get; set;}
       public string Name {get; set;}
    }

    //classe non définie dans dbcontext comme un dbset
    public class DuplicateCustomer:Customer{ 
       public object DuplicateId {get; set;}
    }

    //Bon/Modèle correct*
    //Classe de base commune
    public class CustomerBase{ 
       public int Id {get; set;}
       public string Name {get; set;}
    }

    //modèle d'entité référencé dans dbcontext comme un dbset
    public class Customer: CustomerBase{

    }

    //modèle d'entité non référencé dans dbcontext comme un dbset
    public class DuplicateCustomer:CustomerBase{

       public object DuplicateId {get; set;}

    }

7voto

meataxe Points 148

Un autre scénario où cela se produit est lorsque vous avez une classe de base et une ou plusieurs sous-classes, où au moins l'une des sous-classes introduit des propriétés supplémentaires :

class Folder {
  [key]
  public string Id { get; set; }

  public string Name { get; set; }
}

// N'ajoute pas de propriétés, mais provient d'une vue différente dans la base de données que Folder :
class SomeKindOfFolder: Folder {
}

// Ajoute certaines propriétés, mais provient d'une vue différente dans la base de données que Folder :
class AnotherKindOfFolder: Folder {
  public string FolderAttributes { get; set; }
}

Si ceux-ci sont mappés dans le DbContext comme ci-dessous, l'erreur "'Nom de colonne non valide 'Discriminator'" se produit lorsque tout type basé sur le type de base Folder est accédé :

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity().ToTable("Tous_Les_Dossiers");
  modelBuilder.Entity().ToTable("Certains_Types_De_Dossiers");
  modelBuilder.Entity().ToTable("Autres_Types_De_Dossiers");
}

J'ai trouvé que pour résoudre le problème, nous extrayons les propriétés de Folder vers une classe de base (qui n'est pas mappée dans OnModelCreating()) comme ceci - OnModelCreating devrait rester inchangé :

class FolderBase {
  [key]
  public string Id { get; set; }

  public string Name { get; set; }
}

class Folder: FolderBase {
}

class SomeKindOfFolder: FolderBase {
}

class AnotherKindOfFolder: FolderBase {
  public string FolderAttributes { get; set; }
}

Cela élimine le problème, mais je ne sais pas pourquoi !

0 votes

Merci, meataxe - cela m'a coûté une heure ou deux, mais le pire, c'est que j'ai dû avoir ce problème auparavant, car j'avais déjà créé toutes les classes de base. Un moi plus idiot un an plus tard se dit, "Hé, on dirait que cette classe de base ne sert à rien. Je pense que je vais juste la supprimer..." Et voilà une heure de ma vie perdue que je ne récupérerai jamais. POURQUOI EST-CE REQUIS? J'aimerais mieux comprendre EF.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X