简体   繁体   English

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

[英]EF5 Two level inheritance, TPT + TPH (Code first)

I've implemented a simple inheritance using EF5 code-first TPT. 我已经使用EF5代码优先的TPT实现了简单的继承。 My base class is "Person" and my inherited class is "User". 我的基类是“人”,而继承的类是“用户”。 Tables are named accordingly. 表被相应地命名。

Due to the fact my software is gonna be reused as a framework for other people to build on top of it, I want to give them an easy tool to expand the software without changing the database. 由于我的软件将被复用为其他人在其上构建的框架,因此,我想给他们一个简单的工具来扩展软件而无需更改数据库。 I need only to fetch the right class type, I won't need to update it. 我只需要获取正确的类类型,就不需要更新它。 In order to achieve this I want a specialized layer for Users with TPH. 为了实现这一目标,我希望为具有TPH的用户提供专门的层。 Developers will add their class in code and insert their record marking the type. 开发人员将在代码中添加其类,并插入标记该类型的记录。

I've added to Users table the "Discriminator" field but now I'm getting this error trying to load the model: 我已经在“用户”表中添加了“ Discriminator”字段,但是现在我在尝试加载模型时遇到此错误:

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

It's not really clear what the error means.. Can anyone suggest an explanation/solution for it? 目前尚不清楚该错误是什么意思。有人可以提出解释/解决方案吗?

Thanks in advance 提前致谢

After some digging, I found out that it's possible indeed, I'm not sure since which version but it works like a charm on EF5. 经过一番挖掘,我发现确实有可能,我不确定从哪个版本开始,但是它在EF5上的作用就像是一种魅力。

The solution to the above error is to Map the relation manually with Fluent API. 解决上述错误的方法是使用Fluent API手动映射关系。 The TPT layer requires: TPT层要求:

  • An inheritance relationship between two classes 两个类之间的继承关系
  • The same primary key on both tables, and for the inherited type table a foreign key on the primary key. 两个表上具有相同的主键,对于继承的类型表,主键上具有外键。

Here is the class definitions: 这是类的定义:

[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
{
}

The TPH layer doesn't seem to work just adding the "Discriminator" field. 仅添加“ Discriminator”字段,TPH层似乎不起作用。 The solution I found is: 我找到的解决方案是:

  • Adding a "UserType" field to the database. 向数据库添加“ UserType”字段。
  • Map it using Fluent API. 使用Fluent API对其进行映射。

An important note to make is that the "UserType" field cannot be included in class definition or you'll get the error above. 需要注意的重要一点是,“ UserType”字段不能包含在类定义中,否则您将得到上面的错误。

public class CustomUser : User
{
}

Inside the DbContext class, on the OnModelCreating override: 在DbContext类中,在OnModelCreating重写上:

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

Finally, having this kind of code in DbContext is not really reusable, so I've moved it inside a EntityTypeConfiguration class as following: 最后,在DbContext中具有这种代码实际上不是可重用的,因此我将其移至EntityTypeConfiguration类中,如下所示:

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

The DbContext can now use a little of reflection to load all the EntityTypeConfiguration classes 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 });
        }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM