Код EF сначала «Неверное имя столбца« Дискриминатор »», но нет наследования

154

В моей базе данных есть таблица SEntries (см. Ниже оператор CREATE TABLE). У него есть первичный ключ, пара внешних ключей и ничего особенного. В моей базе данных много таблиц, похожих на эту, но по какой-то причине в этой таблице появился столбец «Дискриминатор» в прокси-классе EF.

Вот как класс объявлен в 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; }
            ...
    }

Когда я пытаюсь добавить новую строку в эту таблицу, я получаю сообщение об ошибке:

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

Эта проблема возникает, только если вы наследуете свой класс C # от другого класса, но SEntry не наследует ни от чего (как вы можете видеть выше).

В дополнение к этому, когда я получаю всплывающую подсказку отладчика, когда нахожу курсор мыши на экземпляр EMData для свойства SEntries, он отображает:

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

Любые предложения или идеи, где можно добраться до сути этого вопроса? Я попытался переименовать таблицу, первичный ключ и несколько других вещей, но ничего не работает.

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
Марсело Кальбуччи
источник
16
Для следующего человека, который потратит некоторое время на попытки выяснить это, произошло то, что в другом месте кода у меня был класс, унаследованный от SEntry, хотя это не тот класс, который когда-либо будет храниться в БД , Так что все, что мне нужно было сделать, это добавить [NotMapped] в качестве атрибута этого класса!
Марсело Кальбуччи
Я получаю эту ошибку, если я не помещаю [NotMapped] в класс ApplicationUser в Identitymodel.cs
Heemanshu Bhalla

Ответы:

319

Оказывается, что Entity Framework будет предполагать, что любой класс, который наследуется от класса POCO, который сопоставлен с таблицей в базе данных, требует столбец Discriminator, даже если производный класс не будет сохранен в БД.

Решение довольно простое, и вам просто нужно добавить его [NotMapped]в качестве атрибута производного класса.

Пример:

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

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

Теперь, даже если вы сопоставите класс Person с таблицей Person в базе данных, столбец «Дискриминатор» не будет создан, поскольку производный класс имеет [NotMapped].

В качестве дополнительного совета вы можете использовать [NotMapped]свойства, которые вы не хотите отображать в поле в БД.

Марсело Кальбуччи
источник
7
хорошо, так проходит 3 часа моей жизни; (но все равно tyvm. Я должен также добавить просто, чтобы быть ясным ... производные классы могут быть полностью в углу, ни в коем случае не используемые re: persistence и EF все равно попробую их нарисовать ... очень запутанно.
Rism
12
Если вы не нашли [NotMapped], добавьте ссылку на «System.ComponentModel.DataAnnotations» в проект из «Assembly Framework».
XandrUu
9
using System.ComponentModel.DataAnnotations.Schema;
Ygaradon
6
но в моем случае я унаследовал класс для добавления столбца в таблицу базы данных, используя дочерний класс. Поэтому я не могу использовать этот атрибут notmapped, чтобы он работал. Каким должно быть мое решение в этом случае?
Сохаиб Джавед
4
в моем случае добавление не отображенных не помогло. я не нанесен на карту во всех моделях представления
Heemanshu Bhalla
44

Вот синтаксис 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);

}
Вальтер Стабош
источник
Не лучше ли добавить [NotMapped]атрибут?
Кит
1
@ Мой ответ таков: как игнорировать столбец с помощью Fluent API, в котором не используются такие атрибуты, как [NotMapped]
Walter Stabosz
1
Кейт, это предпочтительный ответ, я думаю, потому что сейчас мы движемся к стандарту кода сначала, и ответ Уолтера лучше подходит для этого сценария, особенно если вы в конечном итоге используете db-миграции.
Тахир Халид
Там, где у вас есть противоположная проблема (то есть и связанный класс EF, который наследуется от класса POCO), это был единственный способ заставить это работать без загрязнения модели данных EF.
Пол Майклз
8

Я только что столкнулся с этим, и моя проблема была вызвана наличием двух сущностей, System.ComponentModel.DataAnnotations.Schema.TableAttributeссылающихся на одну и ту же таблицу.

например:

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

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

изменение второй из fooк foo_extendedисправил для меня , и я сейчас , используя таблицу для каждого типа (TPT)

Seph
источник
Это не сработало для меня:The entity types 'AtencionMedica' and 'AtencionMedicaAP' cannot share table 'AtencionMedicas' because they are not in the same type hierarchy
Джеймс Реатеги
Спасибо, помогло мне, возникла та же проблема с использованием свободного API:, var entity = modelBuilder.Entity<EntityObject>().ToTable("ENTITY_TABLE")а затем другая строка, использующая то же самое EntityObjectили то же самое ENTITY_TABLE.
Mathijs Flietstra
4

Другой сценарий, в котором это происходит, - это когда у вас есть базовый класс и один или несколько подклассов, где хотя бы один из подклассов вводит дополнительные свойства:

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

Если они сопоставлены, DbContextкак показано ниже, ошибка «Недопустимое имя столбца« Дискриминатор »» возникает при Folderдоступе к любому типу на основе базового типа:

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

Я обнаружил, что для решения этой проблемы мы извлекаем реквизиты Folderдля базового класса (который не отображается OnModelCreating()) следующим образом - OnModelCreatingдолжны быть неизменными:

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

Это устраняет проблему, но я не знаю почему!

meataxe
источник
спасибо, meataxe - это стоило мне часа или двух, но хуже всего было то, что у меня раньше была эта проблема, потому что у меня были настроены базовые классы. Еще тупее меня год спустя говорит: «Эй, похоже, этот базовый класс ничего не делает. Думаю, я просто уберу это…» И прошел час моей жизни, который я никогда не верну. ПОЧЕМУ ЭТО ТРЕБУЕТСЯ? Хотел бы я лучше понимать EF.
Wellspring
2

Я получаю ошибку в другой ситуации, и вот проблема и решение:

У меня есть 2 класса, производные от одного базового класса с именем LevledItem:

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

Но в их DbContext я скопировал некоторый код, но забыл изменить одно из названий классов:

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

Да, вторая Карта <Команда> должна быть Карта <История>. И мне понадобилось полдня, чтобы понять это!

Cheny
источник
2

У меня была похожая проблема, не совсем те же условия, и тогда я увидел этот пост . Надеюсь, это кому-нибудь поможет. Очевидно, я использовал одну из моих моделей сущностей EF - базовый класс для типа, который не был указан как набор БД в моем dbcontext. Чтобы решить эту проблему, мне пришлось создать базовый класс, который имел все свойства, общие для двух типов, и наследовал от нового базового класса среди двух типов.

Пример:

//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
источник
1

эта ошибка произошла со мной, потому что я сделал следующее

  1. Я изменил имя столбца таблицы в базе данных
  2. (Я не использовал Update Model from databaseв Edmx) я переименовал вручную имя свойства, чтобы соответствовать изменению в схеме базы данных
  3. Я провел некоторый рефакторинг, чтобы изменить имя свойства в классе так, чтобы оно совпадало со схемой и моделями базы данных в Edmx.

Хотя все это я получил эту ошибку

так what to do

  1. Я удалил модель из Edmx
  2. Щелкните правой кнопкой мыши и Update Model from database

это восстановит модель, а структура сущности will не give you this error

надеюсь, это поможет вам

Башир Аль-Момани
источник
1

Старый Q, но для потомков ... это также случается (.NET Core 2.1), если у вас есть самореферентное свойство навигации ("Parent" или "Children" того же типа), но имя свойства Id не то, что EF ожидает. То есть у меня в классе было свойство «Id» WorkflowBase, и у него был массив связанных дочерних шагов, которые также имели тип WorkflowBase, и он продолжал пытаться связать их с несуществующим «WorkflowBaseId» (имя i Предположим, что он предпочитает как естественный / обычный по умолчанию). Я должен был явным образом настроить его с помощью HasMany(), WithOne()и HasConstraintName()сказать ему , как пройти. Но я потратил несколько часов, думая, что проблема заключается в «локальном» отображении первичного ключа объекта, который я пытался исправить множеством разных способов, но который, вероятно, всегда работал.

tntwyckoff
источник