Código EF Primeiro "Nome de coluna inválido 'Discriminador'", mas sem herança

154

Eu tenho uma tabela no meu banco de dados chamada SEntries (veja abaixo a instrução CREATE TABLE). Ele possui uma chave primária, algumas chaves estrangeiras e nada de especial. Tenho muitas tabelas no meu banco de dados semelhantes a essa, mas, por algum motivo, essa tabela acabou com uma coluna "Discriminator" na EF Proxy Class.

É assim que a classe é declarada em 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; }
            ...
    }

Quando tento adicionar uma nova linha a essa tabela, recebo o erro:

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

Esse problema ocorre apenas se você estiver herdando sua classe C # de outra classe, mas o SEntry não está herdando nada (como você pode ver acima).

Além disso, quando recebo a dica de ferramenta no depurador quando passo o mouse sobre a instância EMData da propriedade SEntries, ela exibe:

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...

Alguma sugestão ou idéia de onde chegar ao fundo desta edição? Tentei renomear a tabela, a chave primária e algumas outras coisas, mas nada funciona.

Tabela 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
fonte
16
Para a próxima pessoa que passará algum tempo tentando descobrir isso, o que aconteceu é que, em outro lugar no código, eu tive uma classe que herdou do SEntry, mesmo que não seja uma classe que jamais seria armazenada no banco de dados . Então, tudo o que eu precisava fazer era adicionar [NotMapped] como um atributo dessa classe!
Marcelo Calbucci
Estou recebendo esse erro se eu não colocar [NotMapped] na classe ApplicationUser no Identitymodel.cs
Heemanshu Bhalla

Respostas:

319

Acontece que o Entity Framework assumirá que qualquer classe que herda de uma classe POCO que é mapeada para uma tabela no banco de dados requer uma coluna Discriminator, mesmo que a classe derivada não seja salva no DB.

A solução é bastante simples e você só precisa adicionar [NotMapped]como um atributo da classe derivada.

Exemplo:

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

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

Agora, mesmo se você mapear a classe Person para a tabela Person no banco de dados, uma coluna "Discriminator" não será criada porque a classe derivada possui [NotMapped].

Como uma dica adicional, você pode usar [NotMapped]para propriedades que não deseja mapear para um campo no banco de dados.

Marcelo Calbucci
fonte
7
ok então passa 3 horas da minha vida; (mas tyvm é o mesmo. Devo acrescentar apenas para ficar claro ... as classes derivadas podem estar por todo o canto, não sendo usadas re: persistence e EF ainda vai tentar atraí-los ... muito confuso.
RISM
12
Se você não encontrar [NotMapped], adicione uma referência a: "System.ComponentModel.DataAnnotations" ao projeto em "Assembly Framework".
XandrUu
9
using System.ComponentModel.DataAnnotations.Schema;
Ygaradon
6
mas no meu caso, herdei uma classe para adicionar coluna na tabela db usando a classe filho. Portanto, não posso usar esse atributo não mapeado para fazê-lo funcionar. Qual deve ser minha solução nesse caso?
Sohaib javed
4
no meu caso, adicionar não mapeado não ajudou. i ter notmapped em todos viewmodels
Heemanshu Bhalla
44

Aqui está a sintaxe da 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
fonte
Não seria melhor apenas adicionar o [NotMapped]atributo?
Keith
1
@Keith minha resposta é como ignorar uma coluna usando a API Fluent, que não use atributos como [notmapped]
Walter Stabosz
1
Keith, esta é a resposta preferida, eu acho, porque agora estamos migrando para um padrão de código primeiro e a resposta de Walter é mais adequada para esse cenário, especialmente se você acabar usando migrações de banco de dados.
Tahir Khalid
Onde você tem o problema oposto (isto é, a classe vinculada EF que herda de uma classe POCO), era a única maneira de conseguir que isso funcionasse sem poluir o modelo de dados com EF.
Paul Michaels
8

Acabei de encontrar isso e meu problema foi causado por ter duas entidades, ambas System.ComponentModel.DataAnnotations.Schema.TableAttributereferindo-se à mesma tabela.

por exemplo:

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

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

alterando o segundo de foopara foo_extendedcorrigido isso para mim e agora estou usando a tabela por tipo (TPT)

Seph
fonte
Isso não funcionou para mim:The entity types 'AtencionMedica' and 'AtencionMedicaAP' cannot share table 'AtencionMedicas' because they are not in the same type hierarchy
James Reategui 08/09
Obrigado, me ajudou, tive o mesmo problema usando a API fluente: var entity = modelBuilder.Entity<EntityObject>().ToTable("ENTITY_TABLE")e, em seguida, outra linha usando o mesmo EntityObjectou o mesmo ENTITY_TABLE.
Mathijs Flietstra
4

Outro cenário em que isso ocorre é quando você tem uma classe base e uma ou mais subclasses, em que pelo menos uma das subclasses apresenta propriedades extras:

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; }
}

Se eles estiverem mapeados da DbContextseguinte maneira, o erro "'Nome da coluna inválido' Discriminador '" ocorrerá quando qualquer tipo com Folderbase no tipo base for acessado:

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");
}

Descobri que, para corrigir o problema, extraímos os adereços de Folderuma classe base (que não é mapeada OnModelCreating()) como tal - OnModelCreatingdeve permanecer inalterada:

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; }
}

Isso elimina o problema, mas não sei por que!

meataxe
fonte
obrigado, meataxe - isso me custou uma ou duas horas, mas a pior parte foi que eu já devia ter esse problema antes, porque eu tinha as classes base todas configuradas. Dumber me um ano depois diz: "Ei, parece que essa classe base não está fazendo nada. Acho que vou removê-la ..." E passou uma hora da minha vida que nunca mais voltarei. POR QUE ISSO É NECESSÁRIO? Eu gostaria de entender melhor a EF.
Wellspring
2

Eu recebo o erro em outra situação, e aqui estão o problema e a solução:

Eu tenho 2 classes derivadas de uma mesma classe base chamada LevledItem:

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

Mas no DbContext, copiei algum código, mas esqueço de alterar um dos nomes da 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));
    }

Sim, o segundo mapa <equipe> deve ser o mapa <história>. E me custou meio dia para descobrir!

Cheny
fonte
2

Eu tive um problema semelhante, não exatamente as mesmas condições e então vi este post . Espero que ajude alguém. Aparentemente, eu estava usando um dos meus modelos de entidade EF uma classe base para um tipo que não foi especificado como um db definido no meu dbcontext. Para corrigir esse problema, tive que criar uma classe base que tivesse todas as propriedades comuns aos dois tipos e herdar a nova classe base entre os dois tipos.

Exemplo:

//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
fonte
1

esse erro aconteceu comigo porque eu fiz o seguinte

  1. Alterei o nome da coluna da tabela no banco de dados
  2. (Não usei Update Model from databaseno Edmx) Renomeei manualmente Nome da propriedade para corresponder à alteração no esquema do banco de dados
  3. Fiz algumas refatorações para alterar o nome da propriedade na classe para ser o mesmo que esquema e modelos de banco de dados no Edmx

Embora tudo isso, eu recebi esse erro

tão what to do

  1. Eu apaguei o modelo do Edmx
  2. Clique com o botão direito e Update Model from database

isso irá regenerar o modelo, e a estrutura da entidade will não give you this error

espero que isso ajude você

Basheer AL-MOMANI
fonte
1

Q antigo, mas para a posteridade ... também acontece (.NET Core 2.1) se você tiver uma propriedade de navegação de referência própria ("Pai" ou "Filhos" do mesmo tipo), mas o nome da propriedade Id não for o que A EF espera. Ou seja, eu tinha uma propriedade "Id" na minha classe chamada WorkflowBasee ela tinha uma matriz de etapas filho relacionadas, que também eram do tipo WorkflowBase, e continuava tentando associá-las a um "WorkflowBaseId" inexistente (o nome i suponha que prefira como padrão natural / convencional). Eu tive que configurá-lo explicitamente usando HasMany(), WithOne()e HasConstraintName()contá-la como atravessar. Mas passei algumas horas pensando que o problema estava no mapeamento 'local' da chave primária do objeto, que tentei corrigir de várias maneiras diferentes, mas que provavelmente estava sempre funcionando.

tntwyckoff
fonte