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

154

J'ai une table dans ma base de données appelée SEntries (voir ci-dessous l'instruction CREATE TABLE). Il a une clé primaire, quelques clés étrangères et rien de spécial à ce sujet. J'ai de nombreuses tables dans ma base de données similaires à celle-là, 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<SEntry> SEntries { get; set; }
            ...
    }

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

System.Data.SqlClient.SqlException: Invalid column name 'Discriminator'.

Ce problème se produit uniquement 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 passe la souris sur l'instance EMData pour la propriété SEntries, elle affiche:

base {System.Data.Entity.Infrastructure.DbQuery<EM.SEntry>} = {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...

Avez-vous des suggestions ou des idées pour aller au fond de ce problème? J'ai essayé de renommer la table, la clé primaire et quelques autres choses, mais rien ne fonctionne.

Table SQL:

CREATE 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
Marcelo Calbucci
la source
16
Pour la personne suivante qui passera du temps à essayer de comprendre cela, ce qui s'est passé, c'est qu'à un autre endroit du code, j'avais une classe qui a hérité de SEntry, même si ce n'est pas une classe qui serait jamais stockée sur la base de données . Donc, tout ce que j'avais à faire était d'ajouter [NotMapped] comme attribut de cette classe!
Marcelo Calbucci
J'obtiens cette erreur si je ne mets pas [NotMapped] sur la classe ApplicationUser dans Identitymodel.cs
Heemanshu Bhalla

Réponses:

319

Il s'avère qu'Entity Framework supposera que toute classe qui hérite d'une classe POCO mappée à une table de 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]comme attribut de la classe dérivée.

Exemple:

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

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

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

Comme astuce supplémentaire, vous pouvez utiliser [NotMapped]les propriétés que vous ne souhaitez pas mapper à un champ de la base de données.

Marcelo Calbucci
la source
7
ok donc il y a 3 heures de ma vie; (mais tyvm tout de même. Je devrais aussi ajouter juste pour être clair ... les classes dérivées peuvent être complètement dans le coin, pas du tout utilisées re: persistence et EF sera toujours essayer de les attirer ... très déroutant.
risme
12
Si vous ne trouvez pas [NotMapped], veuillez ajouter une référence à: "System.ComponentModel.DataAnnotations" au projet à partir de "Assembly Framework".
XandrUu
9
using System.ComponentModel.DataAnnotations.Schema;
ygaradon
6
mais dans mon cas, j'ai hérité d'une classe pour ajouter une colonne dans la table db en utilisant la classe enfant. Je ne peux donc pas utiliser cet attribut notmapped pour le faire fonctionner. Quelle devrait être ma solution dans ce cas?
sohaib javed
4
dans mon cas, l'ajout de non mappé n'a pas aidé. je n'ai pas cartographié dans tous les modèles de vue
Heemanshu Bhalla
44

Voici la syntaxe de l'API Fluent.

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)
{
    // ignore a type that is not mapped to a database table
    modelBuilder.Ignore<PersonViewModel>();

    // ignore a property that is not mapped to a database column
    modelBuilder.Entity<Person>()
        .Ignore(p => p.FullName);

}
Walter Stabosz
la source
Ne serait-il pas préférable d'ajouter simplement l' [NotMapped]attribut?
Keith
1
@Keith ma réponse est de savoir comment ignorer une colonne à l'aide de l'API Fluent, qui n'utilise pas d'attributs tels que [NotMapped]
Walter Stabosz
1
Keith c'est la réponse préférée, je pense, car nous nous dirigeons maintenant vers une norme de code d'abord 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.
Tahir Khalid
Là où vous avez le problème opposé (c'est-à-dire une classe liée à EF qui hérite d'une classe POCO), c'était la seule façon dont je pouvais faire fonctionner cela sans polluer le modèle de données avec EF.
Paul Michaels
8

Je viens de rencontrer cela et mon problème a été causé par le fait d'avoir deux entités toutes les deux System.ComponentModel.DataAnnotations.Schema.TableAttributefaisant référence à la même table.

par exemple:

[Table("foo")]
public class foo
{
    // some stuff here
}

[Table("foo")]
public class fooExtended
{
    // more stuff here
}

changer le second de fooà foo_extendedcorrigé cela pour moi et j'utilise maintenant la table par type (TPT)

Seph
la source
Cela n'a pas fonctionné pour moi:The entity types 'AtencionMedica' and 'AtencionMedicaAP' cannot share table 'AtencionMedicas' because they are not in the same type hierarchy
James Reategui
Merci, m'a aidé, a eu le même problème en utilisant l'API fluent:, var entity = modelBuilder.Entity<EntityObject>().ToTable("ENTITY_TABLE")puis une autre ligne utilisant soit le même EntityObjectsoit le même ENTITY_TABLE.
Mathijs Flietstra
4

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

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

  public string Name { get; set; }
}

// Adds no props, but comes from a different view in the db to Folder:
class SomeKindOfFolder: Folder {
}

// Adds some props, but comes from a different view in the db to Folder:
class AnotherKindOfFolder: Folder {
  public string FolderAttributes { get; set; }
}

Si ceux-ci sont mappés DbContextcomme ci-dessous, l'erreur "'Nom de colonne non valide' Discriminateur '" se produit lors de l' Folderaccès à un type basé sur le type de base:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Entity<Folder>().ToTable("All_Folders");
  modelBuilder.Entity<SomeKindOfFolder>().ToTable("Some_Kind_Of_Folders");
  modelBuilder.Entity<AnotherKindOfFolder>().ToTable("Another_Kind_Of_Folders");
}

J'ai trouvé que pour résoudre le problème, nous extrayons les accessoires de Folderdans une classe de base (qui n'est pas mappée OnModelCreating()) comme ceci - OnModelCreatingdevrait être inchangée:

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!

hache de viande
la source
merci, meataxe - cela m'a coûté une heure ou deux, mais le pire, c'est que j'ai dû avoir ce problème avant, car j'avais toutes les classes de base installées. Dumber me dit un an plus tard, "Hé, on dirait que cette classe de base ne fait rien. Je pense que je vais juste l'enlever ..." Et il y a passé une heure de ma vie que je ne reviendrai jamais. POURQUOI CELA EST-IL REQUIS? J'aimerais mieux comprendre EF.
Wellspring le
2

J'obtiens l'erreur dans une autre situation, et voici le problème et la solution:

J'ai 2 classes dérivées d'une même classe de base nommée LevledItem:

public partial class Team : LeveledItem
{
   //Everything is ok here!
}
public partial class Story : LeveledItem
{
   //Everything is ok here!
}

Mais dans leur DbContext, j'ai copié du code mais j'ai oublié de changer l'un des noms de classe:

public class MFCTeamDbContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Other codes here
        modelBuilder.Entity<LeveledItem>()
            .Map<Team>(m => m.Requires("Type").HasValue(ItemType.Team));
    }

public class ProductBacklogDbContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        //Other codes here
        modelBuilder.Entity<LeveledItem>()
            .Map<Team>(m => m.Requires("Type").HasValue(ItemType.Story));
    }

Oui, la deuxième carte <Équipe> doit être Carte <Histoire>. Et ça m'a coûté une demi-journée pour le découvrir!

Cheny
la source
2

J'ai eu un problème similaire, pas exactement les mêmes conditions et puis j'ai vu ce post . J'espère que ça aide quelqu'un. Apparemment, j'utilisais l'un de mes modèles d'entité EF une classe de base pour un type qui n'était pas spécifié comme un ensemble de bases de données 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:

//Bad Flow
    //class defined in dbcontext as a dbset
    public class Customer{ 
       public int Id {get; set;}
       public string Name {get; set;}
    }

    //class not defined in dbcontext as a dbset
    public class DuplicateCustomer:Customer{ 
       public object DuplicateId {get; set;}
    }


    //Good/Correct flow*
    //Common base class
    public class CustomerBase{ 
       public int Id {get; set;}
       public string Name {get; set;}
    }

    //entity model referenced in dbcontext as a dbset
    public class Customer: CustomerBase{

    }

    //entity model not referenced in dbcontext as a dbset
    public class DuplicateCustomer:CustomerBase{

       public object DuplicateId {get; set;}

    }
KwakuCsc
la source
1

cette erreur s'est produite avec moi parce que j'ai fait ce qui suit

  1. J'ai changé le nom de colonne de la table dans la base de données
  2. (Je n'ai pas utilisé Update Model from database dans Edmx) J'ai renommé manuellement le nom de la propriété pour correspondre au changement de schéma de base de données
  3. J'ai fait quelques refactorisations pour changer le nom de la propriété dans la classe pour qu'il soit le même que le schéma et les modèles de base de données dans Edmx

Bien que tout cela, j'ai eu cette erreur

alors what to do

  1. J'ai supprimé le modèle d'Edmx
  2. Clic droit et Update Model from database

cela régénérera le modèle et le cadre d'entité will non give you this error

j'espère que cela vous aidera

Basheer AL-MOMANI
la source
1

Ancien Q, mais pour la postérité ... cela arrive aussi (.NET Core 2.1) si vous avez une propriété de navigation auto-référencée ("Parent" ou "Children" du même type) mais que le nom de la propriété Id n'est pas ce EF attend. Autrement dit, j'avais une propriété "Id" sur ma classe appelée WorkflowBase, et elle avait un tableau d'étapes enfants associées, qui étaient également de type WorkflowBase, et elle essayait de les associer à un "WorkflowBaseId" inexistant (le nom i supposons qu'il préfère comme valeur par défaut naturelle / conventionnelle). Je devais configurer explicitement à l'aide HasMany(), WithOne()et HasConstraintName()pour lui dire comment traverser. Mais j'ai passé quelques heures à penser que le problème résidait dans le mappage `` local '' de la clé primaire de l'objet, ce que j'ai tenté de résoudre de différentes manières mais qui fonctionnait probablement toujours.

tntwyckoff
la source