Código EF Primero "Nombre de columna inválido 'Discriminador'" pero sin herencia

154

Tengo una tabla en mi base de datos llamada SEntries (ver abajo la declaración CREATE TABLE). Tiene una clave principal, un par de claves externas y nada especial. Tengo muchas tablas en mi base de datos similares a esa, pero por alguna razón, esta tabla terminó con una columna "Discriminador" en la Clase Proxy EF.

Así es como se declara la clase 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; }
            ...
    }

Cuando intento agregar una nueva fila a esa tabla, aparece el error:

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

Este problema solo ocurre si está heredando su clase C # de otra clase, pero SEntry no está heredando nada (como puede ver arriba).

Además de eso, una vez que obtengo la información sobre herramientas en el depurador cuando paso el mouse sobre la instancia EMData para la propiedad SEntries, muestra:

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

¿Alguna sugerencia o idea de dónde llegar al fondo de este problema? Intenté cambiar el nombre de la tabla, la clave principal y algunas otras cosas, pero nada funciona.

Tabla 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
fuente
16
Para la siguiente persona que pasará algún tiempo tratando de resolver esto, lo que sucedió es que en otro lugar del código, tuve una clase que heredó de SEntry, a pesar de que no es una clase que alguna vez se almacenaría en el DB . ¡Entonces todo lo que tenía que hacer era agregar [NotMapped] como un atributo de esa clase!
Marcelo Calbucci
Recibo este error si no pongo [NotMapped] en la clase ApplicationUser en Identitymodel.cs
Heemanshu Bhalla

Respuestas:

319

Resulta que Entity Framework supondrá que cualquier clase que herede de una clase POCO que está asignada a una tabla en la base de datos requiere una columna Discriminator, incluso si la clase derivada no se guardará en la base de datos.

La solución es bastante simple y solo necesita agregar [NotMapped]como un atributo de la clase derivada.

Ejemplo:

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

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

Ahora, incluso si asigna la clase Persona a la tabla Persona en la base de datos, no se creará una columna "Discriminador" porque la clase derivada sí [NotMapped].

Como consejo adicional, puede usar [NotMapped]propiedades que no desea asignar a un campo en la base de datos.

Marcelo Calbucci
fuente
77
ok, así que pasaron 3 horas de mi vida; (pero tyvm de todos modos. También debo agregar solo para que quede claro ... las clases derivadas pueden estar en toda la esquina sin usar de ninguna manera re: persistencia y EF aún intentará dibujarlos ... muy confusos.
rism
12
Si no encuentra [NotMapped], agregue una referencia a: "System.ComponentModel.DataAnnotations" al proyecto desde "Assembly Framework".
XandrUu
9
utilizando System.ComponentModel.DataAnnotations.Schema;
ygaradon
66
pero en mi caso heredé una clase para agregar una columna en la tabla db usando la clase secundaria. Así que no puedo usar este atributo no mapeado para que funcione. ¿Cuál debería ser mi solución en este caso?
sohaib javed
44
en mi caso, agregar no mapeado no ayudó. No he mapeado en todos los modelos de vista
Heemanshu Bhalla
44

Aquí está la sintaxis de Fluent API.

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
fuente
¿No sería mejor simplemente agregar el [NotMapped]atributo?
Keith
1
@Keith mi respuesta es cómo ignorar una columna usando la API Fluent, que no usa atributos como [NotMapped]
Walter Stabosz
1
Keith, esta es la respuesta preferida, creo, porque ahora se está moviendo hacia un estándar de código primero y la respuesta de Walter es más adecuada para este escenario, especialmente si terminas usando migraciones db.
Tahir Khalid
Cuando tiene el problema opuesto (es decir, una clase vinculada a EF que hereda de una clase POCO), esta era la única forma en que podía hacer que eso funcionara sin contaminar el modelo de datos con EF.
Paul Michaels
8

Acabo de encontrar esto y mi problema fue causado por tener dos entidades con System.ComponentModel.DataAnnotations.Schema.TableAttributereferencias a la misma tabla.

por ejemplo:

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

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

cambiando el segundo de fooa foo_extendedarreglado esto para mí y ahora estoy usando Tabla por tipo (TPT)

Seph
fuente
Esto no funcionó para mí:The entity types 'AtencionMedica' and 'AtencionMedicaAP' cannot share table 'AtencionMedicas' because they are not in the same type hierarchy
James Reategui
Gracias, me ayudó, tuve el mismo problema al usar la API fluida: var entity = modelBuilder.Entity<EntityObject>().ToTable("ENTITY_TABLE")y luego otra línea que usaba lo mismo EntityObjecto lo mismo ENTITY_TABLE.
Mathijs Flietstra
4

Otro escenario en el que esto ocurre es cuando tiene una clase base y una o más subclases, donde al menos una de las subclases introduce propiedades adicionales:

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 se asignan de la DbContextsiguiente manera, se produce el error "'Nombre de columna no válido' Discriminador '" cuando Folderse accede a cualquier tipo basado en el tipo 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");
}

Descubrí que para solucionar el problema, extraemos los accesorios de Folderuna clase base (que no está asignada OnModelCreating()) de esta manera, no OnModelCreatingdebería modificarse:

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

Esto elimina el problema, ¡pero no sé por qué!

meataxe
fuente
gracias, meataxe: eso me costó una o dos horas, pero lo peor fue que debí haber tenido este problema antes, porque tenía todas las clases base configuradas. Mucho más tonto un año después dice: "Oye, parece que esta clase base no está haciendo nada. Creo que lo eliminaré ..." Y hubo una hora de mi vida que nunca volveré. ¿POR QUÉ ES REQUERIDO? Ojalá entendiera mejor a EF.
Manantial
2

Me sale el error en otra situación, y aquí están el problema y la solución:

Tengo 2 clases derivadas de una misma clase base llamada LevledItem:

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

Pero en su DbContext, copié un código pero olvido cambiar uno de los nombres de clase:

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

Sí, el segundo Mapa <Equipo> debería ser Mapa <Historia>. ¡Y me costó medio día resolverlo!

cheny
fuente
2

Tuve un problema similar, no exactamente las mismas condiciones y luego vi esta publicación . Espero que ayude a alguien. Aparentemente estaba usando uno de mis modelos de entidad EF, una clase base para un tipo que no se especificó como un conjunto de bases de datos en mi dbcontext. Para solucionar este problema, tuve que crear una clase base que tuviera todas las propiedades comunes a los dos tipos y heredar de la nueva clase base entre los dos tipos.

Ejemplo:

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

este error ocurrió conmigo porque hice lo siguiente

  1. Cambié el nombre de la columna de la tabla en la base de datos
  2. (No utilicé Update Model from databaseen Edmx) Cambié el nombre manualmente Nombre de propiedad para que coincida con el cambio en el esquema de la base de datos
  3. Realicé una refactorización para cambiar el nombre de la propiedad en la clase para que sea el mismo que el esquema y los modelos de la base de datos en Edmx

Aunque todo esto, recibí este error

entonces what to do

  1. Eliminé el modelo de Edmx
  2. Haga clic derecho y Update Model from database

esto regenerará el modelo, y el marco de la entidad will no give you this error

espero que esto te ayude

Basheer AL-MOMANI
fuente
1

Antigua Q, pero para la posteridad ... también sucede (.NET Core 2.1) si tiene una propiedad de navegación autorreferenciada ("Principal" o "Secundario" del mismo tipo) pero el nombre de la propiedad Id no es lo que EF espera. Es decir, tenía una propiedad "Id" en mi clase llamada WorkflowBase, y tenía una serie de pasos secundarios relacionados, que también eran de tipo WorkflowBase, y seguía intentando asociarlos con un "WorkflowBaseId" inexistente (el nombre i supongamos que prefiere como un defecto natural / convencional). Tuve que configurar explícitamente el uso HasMany(), WithOne()y HasConstraintName()para decir que la forma de atravesar. Pero pasé unas horas pensando que el problema estaba en el mapeo 'localmente' de la clave primaria del objeto, que intenté arreglar de muchas maneras diferentes, pero que probablemente siempre estaba funcionando.

tntwyckoff
fuente