EF Code First „Nieprawidłowa nazwa kolumny„ Dyskryminator ””, ale bez dziedziczenia

154

Mam tabelę w mojej bazie danych o nazwie SEntries (patrz poniżej instrukcja CREATE TABLE). Ma klucz podstawowy, kilka kluczy obcych i nic w tym specjalnego. Mam wiele tabel w mojej bazie danych podobnych do tej, ale z jakiegoś powodu ta tabela zakończyła się kolumną „Discriminator” w klasie EF Proxy.

Oto jak klasa jest zadeklarowana w 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; }
            ...
    }

Kiedy próbuję dodać nowy wiersz do tej tabeli, pojawia się błąd:

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

Ten problem występuje tylko wtedy, gdy dziedziczysz klasę C # z innej klasy, ale SEntry nie dziedziczy z niczego (jak widać powyżej).

Oprócz tego, gdy otrzymam podpowiedź dotyczącą debugera, po umieszczeniu wskaźnika myszy na instancji EMData dla właściwości SEntries, wyświetla się ona:

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

Jakieś sugestie lub pomysły, gdzie dojść do sedna tego problemu? Próbowałem zmienić nazwę tabeli, klucza podstawowego i kilku innych rzeczy, ale nic nie działa.

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
źródło
16
Dla następnej osoby, która spędzi trochę czasu próbując to rozgryźć, stało się to, że w innym miejscu kodu miałem klasę, która odziedziczyła po SEntry, mimo że nie jest to klasa, która kiedykolwiek byłaby przechowywana w DB . Więc wszystko, co musiałem zrobić, to dodać [NotMapped] jako atrybut tej klasy!
Marcelo Calbucci
Otrzymuję ten błąd, jeśli nie umieszczę [NotMapped] w klasie ApplicationUser w Identitymodel.cs
Heemanshu Bhalla

Odpowiedzi:

319

Okazuje się, że Entity Framework zakłada, że ​​każda klasa, która dziedziczy po klasie POCO, która jest mapowana do tabeli w bazie danych, wymaga kolumny Discriminator, nawet jeśli klasa pochodna nie zostanie zapisana w bazie danych.

Rozwiązanie jest dość proste i wystarczy dodać [NotMapped]jako atrybut klasy pochodnej.

Przykład:

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

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

Teraz, nawet jeśli zamapujesz klasę Person na tabelę Person w bazie danych, kolumna „Discriminator” nie zostanie utworzona, ponieważ klasa pochodna ma [NotMapped].

Jako dodatkową wskazówkę możesz użyć [NotMapped]właściwości, których nie chcesz mapować na pole w bazie danych.

Marcelo Calbucci
źródło
7
ok, więc mijają 3 godziny mojego życia; (ale tyvm mimo wszystko. Dla jasności powinienem też dodać ... klasy pochodne mogą być na całej długości w rogu nieużywane w żaden sposób re: persistence i EF nadal będzie próbował ich przyciągnąć ... bardzo mylące.
rism
12
Jeśli nie znajdziesz [NotMapped], dodaj odniesienie do: „System.ComponentModel.DataAnnotations” do projektu z „Assembly Framework”.
XandrUu
9
using System.ComponentModel.DataAnnotations.Schema;
ygaradon
6
ale w moim przypadku odziedziczyłem klasę, aby dodać kolumnę w tabeli db za pomocą klasy potomnej. Więc nie mogę użyć tego niezamapowanego atrybutu, aby działał. Jakie powinno być moje rozwiązanie w takim przypadku?
sohaib javed
4
w moim przypadku dodanie nie zmapowanego nie pomogło. Nie mapowałem we wszystkich
modelach widoku
44

Oto składnia 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
źródło
Czy nie byłoby lepiej po prostu dodać [NotMapped]atrybut?
Keith
1
@Keith, moja odpowiedź brzmi, jak zignorować kolumnę za pomocą interfejsu Fluent API, który nie używa atrybutów takich jak [NotMapped]
Walter Stabosz
1
Keith, myślę, że jest to preferowana odpowiedź, ponieważ teraz zmierzasz w kierunku standardu opartego na kodzie, a odpowiedź Waltera jest lepiej dopasowana do tego scenariusza, zwłaszcza jeśli w końcu używasz migracji db.
Tahir Khalid
Tam, gdzie masz przeciwny problem (tj. I klasa związana z EF, która dziedziczy z klasy POCO), był to jedyny sposób, w jaki mogłem to uruchomić bez zanieczyszczania modelu danych EF.
Paul Michaels,
8

Właśnie to napotkałem, a mój problem był spowodowany posiadaniem dwóch jednostek, które System.ComponentModel.DataAnnotations.Schema.TableAttributeodnoszą się do tej samej tabeli.

na przykład:

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

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

zmiana drugiej z foona foo_extendednaprawioną dla mnie i teraz używam tabeli według typu (TPT)

Seph
źródło
To nie zadziałało dla mnie:The entity types 'AtencionMedica' and 'AtencionMedicaAP' cannot share table 'AtencionMedicas' because they are not in the same type hierarchy
James Reategui
Dzięki, pomogli mi, miałem ten sam problem przy użyciu Fluent API:, var entity = modelBuilder.Entity<EntityObject>().ToTable("ENTITY_TABLE")a następnie kolejną linię używającą tego samego EntityObjectlub tego samego ENTITY_TABLE.
Mathijs Flietstra
4

Innym scenariuszem, w którym tak się dzieje, jest sytuacja, gdy masz klasę bazową i jedną lub więcej podklas, w których co najmniej jedna z podklas wprowadza dodatkowe właściwości:

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

Jeśli są one odwzorowane w DbContextponiższy sposób, podczas Folderuzyskiwania dostępu do dowolnego typu opartego na typie podstawowym pojawia się błąd „„ Nieprawidłowa nazwa kolumny ”Dyskryminator” :

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

Okazało się, że aby rozwiązać problem, wyodrębniamy właściwości Folderdo klasy bazowej (która nie jest mapowana OnModelCreating()) w ten sposób - OnModelCreatingpowinno pozostać niezmienione:

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

Eliminuje to problem, ale nie wiem dlaczego!

meataxe
źródło
dzięki, meataxe - to kosztowało mnie godzinę lub dwie, ale najgorsze było to, że musiałem mieć ten problem wcześniej, ponieważ miałem ustawione wszystkie podstawowe klasy. Głupi mi rok później mówi: „Hej, wygląda na to, że ta klasa podstawowa nic nie robi. Myślę, że po prostu ją usunę…” I minęła godzina mojego życia, której nigdy nie odzyskam. DLACZEGO JEST TO WYMAGANE? Chciałbym lepiej zrozumieć EF.
Wellspring
2

Błąd pojawia się w innej sytuacji, a oto problem i rozwiązanie:

Mam 2 klasy pochodzące z tej samej klasy bazowej o nazwie LevledItem:

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

Ale w ich DbContext skopiowałem trochę kodu, ale zapomniałem zmienić jedną z nazw klas:

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

Tak, druga mapa <Team> powinna być mapą <Story>. I pół dnia kosztowało mnie to rozgryzienie!

cheny
źródło
2

Miałem podobny problem, nie dokładnie te same warunki i wtedy zobaczyłem ten post . Mam nadzieję, że to komuś pomoże. Najwyraźniej użyłem jednego z moich modeli jednostek EF, klasy bazowej dla typu, który nie został określony jako zestaw db w moim kontekście dbcontext. Aby rozwiązać ten problem, musiałem utworzyć klasę bazową, która miałaby wszystkie właściwości wspólne dla obu typów i dziedziczyła z nowej klasy bazowej spośród dwóch typów.

Przykład:

//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
źródło
1

ten błąd występuje ze mną, ponieważ wykonałem następujące czynności

  1. Zmieniłem nazwę kolumny tabeli w bazie danych
  2. (Nie używałem Update Model from databasew Edmx) Zmieniłem nazwę ręcznie Nazwa właściwości, aby dopasować ją do zmiany w schemacie bazy danych
  3. Dokonałem refaktoryzacji, aby zmienić nazwę właściwości w klasie na taką samą jak schemat i modele bazy danych w Edmx

Chociaż to wszystko, mam ten błąd

więc what to do

  1. Usunąłem model z Edmx
  2. Kliknij prawym przyciskiem i Update Model from database

spowoduje to ponowne wygenerowanie modelu, a struktura jednostki will nie give you this error

mam nadzieję, że to ci pomoże

Basheer AL-MOMANI
źródło
1

Stara Q, ale dla potomnych ... zdarza się również (.NET Core 2.1), jeśli masz odwołującą się do siebie właściwość nawigacji („Parent” lub „Children” tego samego typu), ale nazwa właściwości Id nie jest tym, czym EF oczekuje. Oznacza to, że moja klasa miała właściwość „Id” o nazwie WorkflowBase, która miała tablicę powiązanych kroków podrzędnych, które również były typu WorkflowBase, i próbowała powiązać je z nieistniejącym „WorkflowBaseId” (nazwa i załóżmy, że preferuje jako naturalne / konwencjonalne domyślne). Miałem jawnie skonfigurować go za pomocą HasMany(), WithOne()i HasConstraintName()aby poinformować go jak przemierzać. Spędziłem jednak kilka godzin, myśląc, że problem polega na „lokalnym” mapowaniu klucza podstawowego obiektu, który próbowałem naprawić na kilka różnych sposobów, ale który prawdopodobnie zawsze działał.

tntwyckoff
źródło