Настройка поля Auto Increment в абстрактном классе в Entity Framework

У меня есть несколько первых объектов кода, похожих на эти:

public abstract class Animal {
    public int ID { get; set; }
    public int NumberOfLegs { get; set; }
}

public class Dog : Animal {
    public string OtherDogRelatedStuff { get; set; }
}

public class Bird : Animal {
    public string OtherBirdRelatedStuff { get; set; }
}

public class MyContext : DbContext {

    public IDbSet<Animal> Animals { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {

        var pluraliser = PluralizationService.CreateService(new System.Globalization.CultureInfo("en-GB"));

        modelBuilder.HasDefaultSchema("vs");
        modelBuilder.Types().Configure(t => t.ToTable(pluraliser.Pluralize(t.ClrType.Name)));

        // This next line doesn't give ID column IDENTITY(1,1)
        modelBuilder.Entity<Animal>().Property(_ => _.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        // This line puts IDENTITY(1,1) on Animal.ID, but causes errors when I try to add/update data.
        //modelBuilder.Entity<Dog>().Property(_ => _.ID).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        base.OnModelCreating(modelBuilder);
    }        
}

И мне нужна таблица по типу ... поэтому таблицы выглядят примерно так:

CREATE TABLE Animals (
    ID INT NOT NULL IDENTITY(1,1),
    NumberOfLegs INT NOT NULL,
    CONSTRAINT pkAnimals PRIMARY KEY (ID)
)

CREATE TABLE Dogs (
    ID INT NOT NULL,
    OtherDogRelatedStuff VARCHAR(200),
    CONSTRAINT pkDogs PRIMARY KEY (ID),
    CONSTRAINT fkAnimal_Dog FOREIGN KEY (ID) REFERENCES Animals(ID)
)

CREATE TABLE Birds (
    ID INT NOT NULL,
    OtherBirdRelatedStuff VARCHAR(200),
    CONSTRAINT pkBirds PRIMARY KEY (ID),
    CONSTRAINT fkAnimal_Bird FOREIGN KEY (ID) REFERENCES Animals(ID)
)

Данные выглядят так:

---Animals----------
ID      NumberOfLegs
1       4
2       4
3       2

---Dogs---------------------
ID      OtherDogRelatedStuff
1       Woof1
2       Woof2

---Birds---------------------
ID      OtherCatRelatedStuff
3       Sqwark1

Но я не могу заставить идентификатор автоматического увеличения работать или правильно настроен.

Я пробовал это:

modelBuilder.Entity<Animal>().Property(_ => _.ID)
    .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

Но поскольку Animal является абстрактным, похоже, это не устанавливает свойство IDENTITY(1,1) в таблице.

Я также пробовал сделать то же самое с сущностью Dog, которая правильно добавляет свойство идентификатора в столбец идентификатора в таблице Animals, но я получаю UpdateException, когда пытаюсь добавить новые сущности в базу данных с помощью метода SaveChanges():

A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: 'ID'.

Как правильно настроить столбец идентификатора абстрактного типа на автоматическое увеличение и заставить его работать при добавлении данных?


person BG100    schedule 28.01.2016    source источник
comment
Свяжите соответствующие части вашего класса dbcontext, пожалуйста   -  person Alexander Derck    schedule 28.01.2016
comment
@AlexanderDerck: Готово.   -  person BG100    schedule 29.01.2016
comment
Заметили, что у вас есть ID и у животного, и у собаки, а Dog наследует его от Animal   -  person Alexander Derck    schedule 29.01.2016
comment
Ах да, это ошибка, когда я написал вопрос. У моего настоящего класса нет идентификатора в конкретных классах. Я это исправлю.   -  person BG100    schedule 29.01.2016
comment
Я также добавлю второй конкретный класс, чтобы еще больше прояснить, как выглядит моя настоящая структура.   -  person BG100    schedule 29.01.2016
comment
Я думаю, modelBuilder.Types().Configure(t => t.ToTable(pluraliser.Pluralize(t.ClrType.Name))); также отображает Animal в таблицу, чего не должно происходить. Вы можете только сопоставить производные классы с таблицей   -  person Alexander Derck    schedule 29.01.2016
comment
Тоже не работает. Я изменил его на это: modelBuilder.Types().Where(t => t != typeof(Animal)).Configure(t => t.ToTable(pluraliser.Pluralize(t.ClrType.Name)));, но столбец ID в таблице Animals не получает IDENTITY (1,1)   -  person BG100    schedule 29.01.2016
comment
Тогда я не знаю, что вызывает вашу проблему, боюсь, для меня этот подход всегда работает: / Однако я всегда использую DbSet вместо IDbSet, и вместо сопоставления всех типов с таблицей я делаю это для каждой Entity, так что вроде modelBuilder.Entity<Dog>().ToTable("Dogs");. Я не думаю, что это вызывает проблему, но кто знает ...   -  person Alexander Derck    schedule 29.01.2016


Ответы (3)


Я бы сказал, используйте интерфейс или отдельный абстрактный класс, чтобы добавить такие общие функции:

public interface Common{
  int ID { get; set; }
}
public class Animal : Common {
[DatabaseGenerated(System.ComponentModel.DataAnnotations.Schema.DatabaseGeneratedOption.Identity)]    
public int ID { get; set; }
public int NumberOfLegs { get; set; }
}

public class Dog : Common {
    public int ID { get; set; }
    public string OtherDogRelatedStuff { get; set; }
}
person Tanveer Ali    schedule 28.01.2016
comment
Спасибо за ответ, но я не этого хочу. Мне нужна таблица Animal с первичным ключом auto-inc, который сопоставляется с абстрактным типом, и 1 или несколько таблиц с одним и тем же идентификатором, связанным внешним ключом, которые наследуют Animal. - person BG100; 29.01.2016
comment
Просто возьмите указанный атрибут и поместите его в свой класс Animal Abstract. - person Shaun Sharples; 29.01.2016
comment
Я уже пробовал это ... не имеет значения, укажу ли я атрибутом в классе или сделаю это в OnModelCreating ... тот же результат. - person BG100; 29.01.2016
comment
На самом деле, добавление атрибута [DatabaseGenerated(...)] сработало ... Не знаю, почему этого не произошло в первый раз, когда я попробовал. Но мне не нужен интерфейс. Спасибо. - person BG100; 29.01.2016

С этими моделями ...

public abstract class Animal
{
    public int ID { get; set; }
    public int NumberOfLegs { get; set; }
}

public class Dog : Animal
{
    public string OtherDogRelatedStuff { get; set; }
}

public class Bird : Animal
{
    public string OtherBirdRelatedStuff { get; set; }
}

... и это в контексте ...

public IDbSet<Animal> Animals { get; set; }

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);
    modelBuilder.Entity<Dog>().ToTable("Dogs");
    modelBuilder.Entity<Bird>().ToTable("Birds");
}

... затем в диспетчере пакетов выполните Add-Migration "Animals", затем Update-Database -Script

У вас должно получиться:

CREATE TABLE [dbo].[Animals] (
    [ID] [int] NOT NULL IDENTITY,
    [NumberOfLegs] [int] NOT NULL,
    CONSTRAINT [PK_dbo.Animals] PRIMARY KEY ([ID])
)
CREATE TABLE [dbo].[Dogs] (
    [ID] [int] NOT NULL,
    [OtherDogRelatedStuff] [nvarchar](max),
    CONSTRAINT [PK_dbo.Dogs] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_ID] ON [dbo].[Dogs]([ID])
CREATE TABLE [dbo].[Birds] (
    [ID] [int] NOT NULL,
    [OtherBirdRelatedStuff] [nvarchar](max),
    CONSTRAINT [PK_dbo.Birds] PRIMARY KEY ([ID])
)
CREATE INDEX [IX_ID] ON [dbo].[Birds]([ID])
ALTER TABLE [dbo].[Dogs] 
ADD CONSTRAINT [FK_dbo.Dogs_dbo.Animals_ID] FOREIGN KEY ([ID]) 
REFERENCES [dbo].[Animals] ([ID])
ALTER TABLE [dbo].[Birds] 
ADD CONSTRAINT [FK_dbo.Birds_dbo.Animals_ID] FOREIGN KEY ([ID]) 
REFERENCES [dbo].[Animals] ([ID])
INSERT [dbo].[__MigrationHistory]([MigrationId], [ContextKey], [Model], [ProductVersion])
VALUES (N'etc)

чего ты хочешь, правда?

Ссылка: Наследование с EF Code First: Часть 2 - Таблица по типу (TPT)

Имейте в виду, что иногда вы можете столкнуться с проблемами с полем, созданным с идентификатором, если оно изначально было создано без идентификатора (и наоборот). Вы можете исправить это, отбросив и воссоздав таблицу: https://stackoverflow.com/a/18917348/150342

person Colin    schedule 29.01.2016

Я столкнулся с той же проблемой, и я исправил ее, используя аннотацию данных в абстрактном классе, например:

public abstract class Animal {
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int ID { get; set; }
    public int NumberOfLegs { get; set; }
}
person Desmond    schedule 24.07.2017