0

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

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

我已将“鉴别器”字段添加到用户表中,但现在尝试加载模型时出现此错误:

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

目前还不清楚错误的含义。任何人都可以提出解释/解决方案吗?

提前致谢

4

1 回答 1

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 层似乎无法仅添加“鉴别器”字段。我找到的解决方案是:

  • 将“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 });
        }
    }
}
于 2013-09-17T07:31:56.607 回答