实体框架4.3 - TPH映射和迁移错误

时间:2022-01-20 02:14:25

I'm using Entity Framework 4.3 with code first and manual migrations. I'm trying to map a TPH (table-per-hierarchy) setup which uses two custom discriminator fields. One for the discriminator itself and the other for soft deletes (much like the "where" option in NH class mappings). The exact same setup works just fine in another project which runs on EF 4.2.

我正在使用Entity Framework 4.3,首先是代码迁移和手动迁移。我正在尝试映射使用两个自定义鉴别器字段的TPH(每层次表)设置。一个用于鉴别器本身,另一个用于软删除(很像NH类映射中的“where”选项)。完全相同的设置在另一个在EF 4.2上运行的项目中运行正常。

I get the error when trying to add a migration using the "add-migration" command in the NuGet console. I have tried all combinations of defining the table name - attributes on class, in "OnModelCreating" method, in EntityTypeConfiguration classes etc. My previous migrations which didn't involve complex hierarchy mappings have worked just fine.

尝试使用NuGet控制台中的“add-migration”命令添加迁移时出现错误。我已经尝试了定义表名的所有组合 - 类的属性,“OnModelCreating”方法,EntityTypeConfiguration类等。我以前的迁移不涉及复杂的层次结构映射已经正常工作。

Is there some breaking change in EF 4.3 that I've stumbled upon?

我偶然发现了EF 4.3中的一些重大变化吗?

The code:

代码:

//---- Domain classes ---------------------

public abstract class ParentClass
{
    public string ParentString { get; set; }
}

public class Foo : ParentClass
{
    public string FooString { get; set; }
}

public class Bar : ParentClass
{
    public string BarString { get; set; }
}

//---- Mapping configuration --------------

public class ParentConfiguration : EntityTypeConfiguration<ParentClass>
{
    public ParentConfiguration()
    {
        Map<Foo>(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Foo");
        })
        .ToTable("Parent");

        Map<Bar>(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Bar");
        })
        .ToTable("Parent");
    }
}

//---- Context ----------------------------

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations.Add(new ParentConfiguration());
}

The error:

错误:

System.InvalidOperationException: The type 'Foo' has already been mapped to table 'Parent'. Specify all mapping aspects of a table in a single Map call.
   at System.Data.Entity.ModelConfiguration.Configuration.Types.EntityTypeConfiguration.AddMappingConfiguration(EntityMappingConfiguration mappingConfiguration)
   at System.Data.Entity.ModelConfiguration.Configuration.ModelConfiguration.ReassignSubtypeMappings()
   at System.Data.Entity.DbModelBuilder.Build(DbProviderManifest providerManifest, DbProviderInfo providerInfo)
   at System.Data.Entity.DbModelBuilder.Build(DbConnection providerConnection)
   at System.Data.Entity.Infrastructure.EdmxWriter.WriteEdmx(DbContext context, XmlWriter writer)
   at System.Data.Entity.Migrations.Extensions.DbContextExtensions.<>c__DisplayClass1.<GetModel>b__0(XmlWriter w)
   at System.Data.Entity.Migrations.Extensions.DbContextExtensions.GetModel(Action`1 writeXml)
   at System.Data.Entity.Migrations.Extensions.DbContextExtensions.GetModel(DbContext context)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration, DbContext usersContext)
   at System.Data.Entity.Migrations.DbMigrator..ctor(DbMigrationsConfiguration configuration)
   at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.GetMigrator()
   at System.Data.Entity.Migrations.Design.ToolingFacade.GetPendingMigrationsRunner.RunCore()
   at System.Data.Entity.Migrations.Design.ToolingFacade.BaseRunner.Run()

Mihkel

Mihkel

1 个解决方案

#1


12  

This is a known issue with 4.3 and 4.3.1. (We found it too late to put the fix in 4.3.1.) Luckily there is a fairly simple way to change your code that should make it work.

这是4.3和4.3.1的已知问题。 (我们发现将修复程序放在4.3.1中为时已晚。)幸运的是,有一种相当简单的方法可以更改代码,使其工作。

In a nutshell, you used to be able to make chained map calls on a single EntityConfiguration in 4.1. and 4.2. Something like this pattern:

简而言之,您曾经能够在4.1中的单个EntityConfiguration上进行链式地图调用。和4.2。像这样的模式:

modelBuilder.Entity<Parent>()
    .Map<Foo>(...)
    .Map<Bar>(...);

This doesn't work in 4.3 and instead you have to make each Map call on an EntityConfiguration for that entity. So a pattern something like this:

这在4.3中不起作用,而是必须在该实体的EntityConfiguration上进行每个Map调用。所以这样的模式:

modelBuilder.Entity<Foo>()
   .Map<Foo>(...);

modelBuilder.Entity<Bar>()
   .Map<Bar>(...);

Taking your case specifically, this should work:

专门针对您的情况,这应该工作:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<ParentClass>()
        .ToTable("Parent");

    modelBuilder.Entity<Foo>()
        .Map(m =>
                {
                    m.Requires("IsActive").HasValue(1);
                    m.Requires("Type").HasValue("Foo");
                });

    modelBuilder.Entity<Bar>()
        .Map(m =>
                {
                    m.Requires("IsActive").HasValue(1);
                    m.Requires("Type").HasValue("Bar");
                });
}

(I've removed a few of the generic parameters since they aren't needed, but that's not important.)

(我删除了一些通用参数,因为它们不需要,但这并不重要。)

Doing this using explicit EntityConfigurations you would use something like this:

使用显式EntityConfiguration执行此操作,您将使用以下内容:

public class ParentConfiguration : EntityTypeConfiguration<ParentClass>
{
    public ParentConfiguration()
    {
        ToTable("Parent");
    }
}

public class FooConfiguration : EntityTypeConfiguration<Foo>
{
    public FooConfiguration()
    {
        Map(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Foo");
        });
    }
}

public class BarConfiguration : EntityTypeConfiguration<Bar>
{
    public BarConfiguration()
    {
        Map(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Bar");
        });
    }
}

And then

接着

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations
        .Add(new ParentConfiguration())
        .Add(new FooConfiguration())
        .Add(new BarConfiguration());
}

We plan to fix this in 5.0.

我们打算在5.0中解决这个问题。

#1


12  

This is a known issue with 4.3 and 4.3.1. (We found it too late to put the fix in 4.3.1.) Luckily there is a fairly simple way to change your code that should make it work.

这是4.3和4.3.1的已知问题。 (我们发现将修复程序放在4.3.1中为时已晚。)幸运的是,有一种相当简单的方法可以更改代码,使其工作。

In a nutshell, you used to be able to make chained map calls on a single EntityConfiguration in 4.1. and 4.2. Something like this pattern:

简而言之,您曾经能够在4.1中的单个EntityConfiguration上进行链式地图调用。和4.2。像这样的模式:

modelBuilder.Entity<Parent>()
    .Map<Foo>(...)
    .Map<Bar>(...);

This doesn't work in 4.3 and instead you have to make each Map call on an EntityConfiguration for that entity. So a pattern something like this:

这在4.3中不起作用,而是必须在该实体的EntityConfiguration上进行每个Map调用。所以这样的模式:

modelBuilder.Entity<Foo>()
   .Map<Foo>(...);

modelBuilder.Entity<Bar>()
   .Map<Bar>(...);

Taking your case specifically, this should work:

专门针对您的情况,这应该工作:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<ParentClass>()
        .ToTable("Parent");

    modelBuilder.Entity<Foo>()
        .Map(m =>
                {
                    m.Requires("IsActive").HasValue(1);
                    m.Requires("Type").HasValue("Foo");
                });

    modelBuilder.Entity<Bar>()
        .Map(m =>
                {
                    m.Requires("IsActive").HasValue(1);
                    m.Requires("Type").HasValue("Bar");
                });
}

(I've removed a few of the generic parameters since they aren't needed, but that's not important.)

(我删除了一些通用参数,因为它们不需要,但这并不重要。)

Doing this using explicit EntityConfigurations you would use something like this:

使用显式EntityConfiguration执行此操作,您将使用以下内容:

public class ParentConfiguration : EntityTypeConfiguration<ParentClass>
{
    public ParentConfiguration()
    {
        ToTable("Parent");
    }
}

public class FooConfiguration : EntityTypeConfiguration<Foo>
{
    public FooConfiguration()
    {
        Map(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Foo");
        });
    }
}

public class BarConfiguration : EntityTypeConfiguration<Bar>
{
    public BarConfiguration()
    {
        Map(m =>
        {
            m.Requires("IsActive").HasValue(1);
            m.Requires("Type").HasValue("Bar");
        });
    }
}

And then

接着

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Configurations
        .Add(new ParentConfiguration())
        .Add(new FooConfiguration())
        .Add(new BarConfiguration());
}

We plan to fix this in 5.0.

我们打算在5.0中解决这个问题。