2012-06-28 36 views
1

我试图在具有继承层次的实体之间配置一对一的关系。一对一关系的EF TPT继承映射问题

对于示例,让我们考虑的以下第一继承链:

[Table("A")] 
public abstract class A 
{ 
    public Guid ID { get; set; } 
    ... 
} 

[Table("AA")] 
public class AA : A 
{ 
    ... 
} 

[Table("AB")] 
public class AB : A 
{ 
    ... 
} 

然后,考虑该第二继承链:

[Table("B")] 
public abstract class B 
{ 
    public Guid ID { get; set; } 
} 

[Table("BA")] 
public class BA : B 
{ 
    ... 
} 

[Table("BB")] 
public class BB : B 
{ 
    ... 
} 

添加AA和BA与AA之间的一对一的关系作为主体实体:

[Table("AA")] 
public class AA : A 
{ 
    ... 
    public BA BAChild { get; set; } 
    ... 
} 

[Table("BA")] 
public class BA : B 
{ 
    ... 
    public AA Parent { get; set; } 
    ... 
} 

public class AAConfiguration : EntityTypeConfiguration<AA> 
{ 
    public AAConfiguration() 
    { 
     this.HasRequired(o => o.BAChild) 
      .WithRequiredPrincipal(o => o.Parent); 
    } 
} 

添加一对一关系b切口白内障手术挽AB和BB与AB的主要实体:

[Table("AB")] 
public class AB : A 
{ 
    ... 
    public BB BBChild { get; set; } 
    ... 
} 

[Table("BB")] 
public class BB : B 
{ 
    ... 
    public AB Parent { get; set; } 
    ... 
} 

public class ABConfiguration : EntityTypeConfiguration<AB> 
{ 
    public ABConfiguration() 
    { 
     this.HasRequired(o => o.BBChild) 
      .WithRequiredPrincipal(o => o.Parent); 
    } 
} 

我也希望英孚生成实体A和B表,这样我已经加入并注册以下空EntityTypeConfiguration:

public class AConfiguration : EntityTypeConfiguration<A> 
{ 

} 

public class BConfiguration : EntityTypeConfiguration<B> 
{ 

} 

如果运行这样的代码你会在创建索引期间得到一个bug(看到Unhandled Exception after Upgrading to Entity Framework 4.3.1

因此,让我们做一些棘手的代码并注册一个从SqlServerMigrationSqlGenerator派生的自定义MigrationSqlGenerator,以避免基于我的业务创建索引内斯的命名规则:

public class Configuration : DbMigrationsConfiguration<DataContext> 
{ 
    public Configuration() 
    { 
     ... 
     this.SetSqlGenerator("System.Data.SqlClient", new CustomSqlServerGenerator()); 
     ... 
    } 
} 

public class CustomSqlServerGenerator : SqlServerMigrationSqlGenerator 
{ 
    protected override void Generate(CreateIndexOperation createIndexOperation) 
    { 
     if (createIndexOperation.Columns.Count() == 1 && createIndexOperation.Columns.Any(o => o == "ID")) 
      return; 

     base.Generate(createIndexOperation); 
    } 
} 

public class DataContext : DbContext 
{ 
    static DataContext() 
    { 
     Database.SetInitializer<DataContext>(new MigrateDatabaseToLatestVersion<DataContext, Configuration>()); 
    } 

    ... 
} 

所以,现在是时候生成数据库,使之,我使用下面的代码:

... 
DataContext dataContext = new DataContext(); 
dataContext.Database.Initialize(true); 
... 

现在,如果你看一下生成的数据库,你会看到表BA和BB都有表AB的外键,BA和AA之间没有外键!?!

我可能错过了一些东西,但我看不出这个示例有什么问题。

怎样才能正确生成数据库?

回答

1

在对整个反射器进行了一些深度搜索之后,似乎此行为是一个EntityFramework错误。

我所在的问题System.Data.Entity.ModelConfiguration.Configuration.Mapping.ForeignKeyPrimitiveOperations在以下功能:

private static void UpdatePrincipalTables(DbDatabaseMapping databaseMapping, DbTableMetadata toTable, bool removeFks, EdmAssociationType associationType, EdmEntityType et) 
    { 
     ... 
    } 

当的EntityFramework加载模式,DbDatabaseMapping例如装有基地类型和外键约束被添加到基类型元数据。 EntityFramework还将EdmAssociationType的一个实例存储在外键约束的注释中,以维护所有必需的信息以生成正确的约束。

后来,当在DbDatabaseMapping实例中添加并配置派生类型时,EntityFramework尝试更新关联的相关端以移动派生表上的外键约束。 要标识要更新的外键约束,在tableInfo中使用以下选择器。值是一个包含一个IEnumerable依赖列外键:

fk => fk.DependentColumns.SequenceEqual<DbTableColumnMetadata>(tableInfo.Value); 

不幸的是,在我的案件为AA和BA之间以及AB和BB列“ID”之间的关系用关系。

因此,它们都是第一次更新,以AA元数据替换主表元数据,第二次更新AB元数据。

因此,这两个外键都是以AB作为主表生成的!

我认为在函数参数中传递的EdmEntityType应该用在选择器中,以便与存储在外键约束注释中的关系结束的实体类型进行比较。

+0

感谢您报告此问题并进行详细分析。我已将这个bug添加到我们的EF6时间表的积压中。 –

+0

感谢您查看我的文章,我将等待EF 6发布或使用TPC而不是TPT找到另一个设计 – P0lO

+0

我认为如果您使用独立关联(数据库中不包含在您的独立关联中的独立FK),也许可以解决此问题c#类)。你可以尝试一下,看看这是否适合你?实现它的方法是添加:.Map(m => {});在每次WithRequiredPrincipal调用之后。 –