【问题标题】:Difficulty Concerning EF Code First Fluent API, TPH, and Foreign Keys关于 EF Code First Fluent API、TPH 和外键的困难
【发布时间】:2012-01-18 01:59:19
【问题描述】:

我的数据库中有两个表。一种称为Users,另一种称为WidgetsWidgets 表代表我的代码模型中的 3 个实体。其中一个实体Widget 是另外两个实体WidgetTypeAWidgetTypeB 的父类。 WidgetTypeAWidgetTypeB 都具有指向 User 实体的导航属性,该属性被持久保存到数据库中的 Users 表中。我无法让 Code First 对 WidgetTypeAWidgetTypeB 实体 (UserId) 使用相同的外键。有谁知道如何做到这一点?似乎这应该是 Table Per Hierarchy 映射的常见问题。

我的实体类如下:

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();
    }
}

无论如何,我一直收到错误

列名“UserId1”无效

当我尝试执行以下操作时:

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();
}

不确定这是否可以修复。我总是可以为 Widgets 表指定第二个 UserId 外键,但这似乎毫无意义。也许有办法使用 Fluent API 做到这一点?

【问题讨论】:

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


    【解决方案1】:

    您不能将不同派生实体中定义的属性映射到同一列。那是EF的限制。如果您的WidgetTypeA 具有UserId 属性并且您的WidgetTypeB 具有UserId 属性,则它们必须是数据库中的不同列。如果您将UserIdUser 属性从派生类型移动到父Widget 类型,它应该可以工作。

    【讨论】:

      【解决方案2】:

      我知道它很晚,但希望可以帮助其他读者。

      尽管在 EF6 中不支持使用映射外键这一点 Ladislav 是正确的,但我确实找到了一个有用的解决方法。

      可以定义一个计算列规范,其表达式仅引用原始列。上述描述中的用户 ID。这可以用作 TPH 映射的鉴别器。使用这种方法,列不需要持久化,但可以用于 TPH,原始列可用作外键。

      【讨论】:

        猜你喜欢
        • 2013-10-21
        • 1970-01-01
        • 1970-01-01
        • 2014-09-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-03-02
        • 1970-01-01
        相关资源
        最近更新 更多