简体   繁体   English

关于EF Code First Fluent API,TPH和外键的难度

[英]Difficulty Concerning EF Code First Fluent API, TPH, and Foreign Keys

I have two tables in my database. 我的数据库中有两个表。 One is called Users , and the other is called Widgets . 一个叫做Users ,另一个叫做Widgets The Widgets table represents 3 entities in my code model. Widgets表表示我的代码模型中的3个实体。 One of the entities, Widget , is a parent class for the other two entities, WidgetTypeA and WidgetTypeB . 其中一个实体Widget是另外两个实体WidgetTypeAWidgetTypeB的父类。 Both WidgetTypeA and WidgetTypeB have navigation properties to the User entity, which is persisted to the Users table in the database. WidgetTypeAWidgetTypeB都具有User实体的导航属性,该实体持久保存到数据库中的Users表。 I'm having trouble getting Code First to use the same foreign key for both the WidgetTypeA and WidgetTypeB entities ( UserId ). 我无法让Code First为WidgetTypeAWidgetTypeB实体( UserId )使用相同的外键。 Does anyone know how to do this? 有谁知道如何做到这一点? It seems like it should be a common problem with Table Per Hierarchy mapping. 看起来它应该是Table Per Hierarchy映射的常见问题。

My entity classes are as follows: 我的实体类如下:

public class Widget
{
    public int Id { get; set; }

    public string Name { get; set; }
}

class WidgetMap : EntityTypeConfiguration<Widget>
{
    public WidgetMap()
    {
        ToTable("Widgets");

        HasKey(w => w.Id);
        Property(w => w.Id)
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        Property(w => w.Name)
            .IsRequired()
            .HasMaxLength(75)
            .IsUnicode(true);
    }
}

public class WidgetTypeA : Widget
{
    public int UserId { get; set; }
    public virtual User User { get; set; }

    public string Color { get; set; }
    public int DepthLevel { get; set; }
}

class WidgetTypeAMap : EntityTypeConfiguration<WidgetTypeA>
{
    public WidgetTypeAMap()
    {
        Map(w => w.Requires("WidgetTypeId").HasValue(1));

        HasRequired(w => w.User)
            .WithMany(u => u.WidgetTypeAs)
            .HasForeignKey(w => w.UserId)
            .WillCascadeOnDelete(false);

        Property(w => w.Color)
            .IsOptional()
            .IsUnicode(true)
            .HasMaxLength(75);

        Property(w => w.DepthLevel)
            .IsOptional();
    }
}

public class WidgetTypeB : Widget
{
    public int UserId { get; set; }
    public virtual User User { get; set; }
}

class WidgetTypeBMap : EntityTypeConfiguration<WidgetTypeB>
{
    public WidgetTypeBMap()
    {
        Map(w => w.Requires("WidgetTypeId").HasValue(2));

        HasRequired(w => w.User)
            .WithMany(u => u.WidgetTypeBs)
            .HasForeignKey(w => w.UserId)
            .WillCascadeOnDelete(false);
    }
}

public class User
{
    public int Id { get; set; }
    public string Username { get; set; }
    public int Age { get; set; }

    public virtual ICollection<WidgetTypeA> WidgetTypeAs { get; set; }
    public virtual ICollection<WidgetTypeB> WidgetTypeBs { get; set; }
}

class UserMap : EntityTypeConfiguration<User>
{
    public UserMap()
    {
        ToTable("Users");

        HasKey(u => u.Id);

        Property(u => u.Username)
            .IsRequired()
            .HasMaxLength(75)
            .IsUnicode(true);

        Property(u => u.Age)
            .IsRequired();
    }
}

At any rate, I keep getting the error 无论如何,我一直在收到错误

Invalid column name 'UserId1' 列名称'UserId1'无效

when I try to perform the following operations: 当我尝试执行以下操作时:

using (var entities = new MyEntities())
{
    User u = new User
    {
        Username = "Frank",
        Age = 14
    };
    entities.Users.Add(u);
    entities.SaveChanges();

    WidgetTypeA wa1 = new WidgetTypeA
    {
        Name = "0SDF81",
        UserId = u.Id,
        DepthLevel = 6
    };
    entities.WidgetTypeAs.Add(wa1);
    entities.SaveChanges();
}

Not sure if this can be fixed or not. 不确定这是否可以修复。 I can always specify a second UserId foreign key for the Widgets table, but that seems pointless. 我总是可以为Widgets表指定第二个UserId外键,但这似乎毫无意义。 Perhaps there's a way to do this using Fluent API? 也许有一种方法可以使用Fluent API来做到这一点?

You cannot map properties defined in different derived entities to the same column. 您不能将在不同派生实体中定义的属性映射到同一列。 That is limitation in EF. 这是EF的限制。 If your WidgetTypeA has UserId property and your WidgetTypeB has UserId property they must be different columns in the database. 如果您的WidgetTypeA具有UserId属性而您的WidgetTypeB具有UserId属性,则它们必须是数据库中的不同列。 It should work if you move both UserId and User properties from derived types to the parent Widget type. 如果您将UserIdUser属性从派生类型移动到父Widget类型,它应该工作。

I know its a long way late, but hopefully may help other readers. 我知道它已经很晚了,但希望可以帮助其他读者。

Although Ladislav was correct that using a mapped Foreign Key is not supported in EF6, I did find a useful workaround. 尽管Ladislav在EF6中不支持使用映射的外键是正确的,但我确实找到了一个有用的解决方法。

It is possible to define a computed column specification whose expression simply refers to the original column. 可以定义计算列规范,其表达式只是引用原始列。 Userid in the description above. Userid在上面的描述中。 This can be used as the discriminator for the TPH mapping. 这可以用作TPH映射的鉴别器。 With this approach, the column need not be persisted, but can be used for TPH, with the original column being available for use as a foreign key. 使用这种方法,列不需要持久化,但可以用于TPH,原始列可用作外键。

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

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