EF5两级继承,TPT + TPH(代码优先)

时间:2013-08-30 09:37:24

标签: c# entity-framework inheritance ef-code-first

我使用EF5代码优先TPT实现了一个简单的继承。 我的基类是“Person”,我继承的类是“User”。表格相应命名。

由于我的软件将被重新用作其他人构建的框架,我想给他们一个简单的工具来扩展软件而不需要更改数据库。我只需要获取正确的类类型,我不需要更新它。为了实现这一点,我想为TPH用户提供一个专门的层。开发人员将在代码中添加他们的类,并插入标记类型的记录。

我已将“Discriminator”字段添加到Users表中,但现在我在尝试加载模型时遇到此错误:

Error 3032: Problem in mapping fragments : mapped to the same rows in table

错误意味着什么并不是很清楚。 有人可以建议解释/解决方案吗?

提前致谢

1 个答案:

答案 0 :(得分:1)

经过一番挖掘,我发现确实可能,我不确定自哪个版本,但它在EF5上的功能就像它一样。

上述错误的解决方案是使用Fluent API手动映射关系。 TPT层要求:

  • 两个类之间的继承关系
  • 两个表上的相同主键,以及继承类型表中主键上的外键。

以下是类定义:

[Table("Persons", Schema="MySchema")]
public partial class Person
{
    public int PersonId { get; set; }
    public string Name { get; set; }
}

[Table("Users", Schema = "MySchema")]
partial class User : Person
{
}

TPH层似乎只是添加“Discriminator”字段。我找到的解决方案是:

  • 将“UserType”字段添加到数据库中。
  • 使用Fluent API映射它。

要做的一个重要注意事项是“UserType”字段不能包含在类定义中,否则您将收到上述错误。

public class CustomUser : User
{
}

在DbContext类中,OnModelCreating覆盖:

modelBuilder.Entity<User>().Map<User>(m =>
    {
        m.ToTable("Users");
        m.Requires("UserType").HasValue("User");
    }).Map<CustomUser>(m =>
    {
        m.Requires("UserType").HasValue("CustomUser");
    });

最后,在DbContext中使用这种代码并不是真的可重用,所以我把它移到了EntityTypeConfiguration类中,如下所示:

public class CustomUserConfiguration : EntityTypeConfiguration<CustomUser>
{
    public CustomUserConfiguration()
    {
        Map<CustomUser>(m => m.Requires("UserType").HasValue("CustomUser"));
    }
}

DbContext现在可以使用一点反射来加载所有EntityTypeConfiguration类

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    MethodInfo addMethod = typeof(ConfigurationRegistrar).GetMethods().Single(m => m.Name == "Add" && m.GetGenericArguments().Any(a => a.Name == "TEntityType"));
    IList<Assembly> assemblies = AppDomain.CurrentDomain.GetAssemblies().Where(a => !a.GetName().Name.StartsWith("System") && !a.GetName().Name.StartsWith("Microsoft")).ToList();
    foreach (Assembly assembly in assemblies)
    {
        IList<Type> types = assembly.GetTypes().Where(t => t.BaseType != null && t.BaseType.IsGenericType && t.BaseType.GetGenericTypeDefinition() == typeof(EntityTypeConfiguration<>)).ToList();
        foreach (Type type in types)
        {
            Type entityType = type.BaseType.GetGenericArguments().Single();
            object entityConfig = assembly.CreateInstance(type.FullName);
            addMethod.MakeGenericMethod(entityType).Invoke(modelBuilder.Configurations, new object[] { entityConfig });
        }
    }
}