实体框架Fluent API不考虑基类属性

时间:2014-09-02 08:52:37

标签: c# entity-framework inheritance table-per-hierarchy ef-fluent-api

EF 6.1:

我们刚刚开始了一个有很多pf继承的项目。选定的继承db映射类型是每个层次结构的表。问题是,在尝试使用add-migration生成迁移时,会引发以下错误:

The foreign key component 'VersionId' is not a declared property on type 'SER'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.

以下是课程&使用的配置类:

public class Version : BaseObject
{
    public virtual ICollection<SER> ListOfSER { get; set; }
}

public abstract class AbsractR : BaseObject
{
    public int ParentId { get; set; }
    public int ChildId { get; set; }

    public int VersionId { get; set; }
    public virtual Version Version { get; set; }
}

public class SER : AbstractR
{
    public int SEDId
    {
        get
        {
            return base.ChildId;
        }
        set
        {
            base.ChildId = value;
        }
    }
    public virtual SED SED { get; set; }
}

public abstract class AbstractD : BaseObject
{
}

public class SED : AbstractD
{
    public virtual ICollection<SER> ListOfSER { get; set; }
}


public class SDContext : BaseContext
{
    public DbSet<Version> Versions { get; set; }
    public DbSet<AbstractD> Ds { get; set; }
    public DbSet<AbstractR> Rs { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);
        modelBuilder.Configurations.Add(new VersionConfiguration());

        #region Refs
        modelBuilder.Configurations.Add(new AbstractRConfiguration());
        modelBuilder.Configurations.Add(new SERConfiguration());
        #endregion

        #region Defs
        modelBuilder.Configurations.Add(new AbstractDConfiguration());
        modelBuilder.Configurations.Add(new SEDConfiguration());
        #endregion
    }
}

public class BaseObjectConfiguration<T> : EntityTypeConfiguration<T> where T : BaseObject
{
    public BaseObjectConfiguration()
    {
        #region Key
        this.HasKey(bo => bo.Id);
        #endregion

        #region Properties
        this.Property(bo => bo.Id).IsRequired();
        this.Property(bo => bo.IsDeleted).IsRequired();
        this.Property(bo => bo.LastModificationDate).IsOptional();
        this.Property(bo => bo.OptimisticVersion).IsConcurrencyToken().IsRequired().IsRowVersion();
        this.Property(bo => bo.CreationDate).IsRequired();
        this.Property(bo => bo.DeletionDate).IsOptional();
        #endregion
    }
}

public class VersionConfiguration : BaseObjectConfiguration<Version>
{
    public VersionConfiguration() : base()
    {
        #region Properties
        #endregion

        #region Objects
        this.HasMany(mdv => mdv.ListOfSER).WithRequired().HasForeignKey(ser => ser.VersionId).WillCascadeOnDelete(false);
        #endregion

        #region Table
        this.ToTable("Versions");
        #endregion
    }
}

public class AbstractRConfiguration : BaseObjectConfiguration<AbstractR>
{
    public AbstractRConfiguration()
        : base()
    {
        #region Properties
        this.Property(ser => ser.VersionId).IsRequired();
        #endregion

        #region Objects
        this.HasRequired(ar => ar.Version).WithMany().HasForeignKey(ar => ar.VersionId).WillCascadeOnDelete(false);
        #endregion

        #region Table
        this.ToTable("Refs");
        #endregion
    }
}

public class SERConfiguration : BaseObjectConfiguration<SER>
{
    public SERConfiguration()
        : base()
    {
        #region Properties
        this.Ignore(ser => ser.SEDId);
        #endregion

        #region Objects
        this.HasRequired(ser => ser.SED).WithMany(sed => sed.ListOfSER).HasForeignKey(ser => ser.ChildId).WillCascadeOnDelete(false);
        #endregion

        #region Table
        this.ToTable("Refs");
        #endregion
    }
}

public class AbstractDConfiguration : BaseObjectConfiguration<AbstractD>
{
    public AbstractDConfiguration() : base()
    {
        this.ToTable("Defs");
    }
}

public class SEDConfiguration : BaseObjectConfiguration<SED>
{
    public SEDConfiguration()
        : base()
    {
        #region Properties
        #endregion

        #region Objects
        this.HasMany(sed => sed.ListOfSER).WithRequired(sed => sed.SED).HasForeignKey(sed => sed.ChildId).WillCascadeOnDelete(false);
        #endregion

        #region Table
        this.ToTable("Defs");
        #endregion
    }
}

我知道我们可以使用[ForeignKey]属性来告诉派生类的导航属性应该使用父抽象类中定义的列。我们希望避免使用DataAnnotations。我只是不明白为什么它会抛出这个错误。 “Version”导航属性在AbstractR配置中定义,而不是在SER配置中定义(因为SER继承自AbstractR也应该有效),我是对的吗?

其次,删除版本属性&amp;映射时,SER映射中使用的“ChildId”和“ParentId”属性出现相同的问题。这是一个已知的问题吗?我做错了吗?

PS:为简单起见,已移除ParentId映射,因为它似乎与ChildId映射存在相同的问题。

有谁知道为什么会出现这种问题?


更新

经过一些研究后,似乎Fluent API无法使用基类属性进行映射。是对的吗 ?这是一种通缉行为吗?为什么DataAnnotations能够使用基类属性而不能使用Fluent API?是不是所有基类属性都插入到每个类中,或者是否使用某种装饰模式读取它?

1 个答案:

答案 0 :(得分:3)

只要主体也使用类型基类的导航属性,您就可以使用基类属性作为外键关联。

Principal(Version)声明ListOfSERSER类型的导航属性为Dependent

public class Version : BaseObject
{
    public virtual ICollection<SER> ListOfSER { get; set; }
}

,但配置使用基类属性(VersionId)作为外键关联

public class VersionConfiguration : BaseObjectConfiguration<Version>
{
    public VersionConfiguration()
        : base()
    {
        HasMany(mdv => mdv.ListOfSER)
            .WithRequired()
            .HasForeignKey(ser => ser.VersionId) // -> belongs to base class
            .WillCascadeOnDelete(false);
    }
}

配置ForeignKeyConstraintConfiguration时不允许这样做,请查看代码摘录

foreach (var dependentProperty in dependentPropertyInfos)
{
    var property
        = dependentEnd.GetEntityType() // -> SER
            .GetDeclaredPrimitiveProperty(dependentProperty); // -> VersionId

    if (property == null) // VersionId is not part of SER metamodel
    {
        throw Error.ForeignKeyPropertyNotFound(
            dependentProperty.Name, dependentEnd.GetEntityType().Name);
    }

    dependentProperties.Add(property);
}

解决方案1 ​​会将ListOfSER类型从SER更改为AbstractR

public class Version : BaseObject
{
    public virtual ICollection<AbstractR> ListOfSER { get; set; }
}

这将更有意义,VersionId是在基类上定义的,任何派生类型都应该能够使用这个外键关联,对吧?但遗憾的是,Version类型只允许SERVersion相关联。

定义流畅的api配置时也存在不一致。

public class VersionConfiguration : BaseObjectConfiguration<Version>
{
    public VersionConfiguration()
        : base()
    {
        HasMany(mdv => mdv.ListOfSER)
            .WithRequired() // -> this should be WithRequired(x => x.Version)
            .HasForeignKey(ser => ser.VersionId)
            .WillCascadeOnDelete(false);
    }
}
public class AbstractRConfiguration : BaseObjectConfiguration<AbstractR>
{
    public AbstractRConfiguration()
        : base()
    {
        HasRequired(ar => ar.Version)
            .WithMany() // -> this should be WithMany(x => x.ListOfSER)
            .HasForeignKey(ar => ar.VersionId)
            .WillCascadeOnDelete(false);
    }
}

更重要的是,您不必同时配置。只需将其放在VersionConfigurationAbstractRConfiguration即可。

解决方案2 会将VersionVersionId从基类移至SER,如果您只希望Version关联与SER

public class Version : BaseObject
{
    public virtual ICollection<SER> ListOfSER { get; set; }
}
public abstract class AbstractR : BaseObject
{
}
public class SER : AbstractR
{
    public int VersionId { get; set; }
    public virtual Version Version { get; set; }
}

配置Version配置或SER配置。

public class VersionConfiguration : BaseObjectConfiguration<Version>
{
    public VersionConfiguration()
        : base()
    {
        HasMany(mdv => mdv.ListOfSER)
            .WithRequired(x => x.Version)
            .HasForeignKey(ser => ser.VersionId)
            .WillCascadeOnDelete(false);
    }
}
public class SERConfiguration : BaseObjectConfiguration<SER>
{
    public SERConfiguration()
        : base()
    {
        HasRequired(ar => ar.Version)
            .WithMany(x => x.ListOfSER)
            .HasForeignKey(ar => ar.VersionId)
            .WillCascadeOnDelete(false);
    }
}