简体   繁体   English

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

[英]Entity framework Fluent API does not consider base class properties

EF 6.1 : EF 6.1:

We just started a project that has a lot pf inheritance. 我们刚刚开始了一个有很多pf继承的项目。 The selected inheritance db mapping type is the table per hierarchy. 选定的继承db映射类型是每个层次结构的表。 The problem is that when trying to generate the migration using the add-migration, the following error is thrown : 问题是,当尝试使用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.

Here are the classes & the configuration classes used : 以下是使用的类和配置类:

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

I know we can use the [ForeignKey] attribute to tell that the navigation property on a derived class should use the column defined in the parent abstract class. 我知道我们可以使用[ForeignKey]属性来告诉派生类的导航属性应该使用父抽象类中定义的列。 We would like to avoid using DataAnnotations. 我们希望避免使用DataAnnotations。 I just don't get why it throw this error. 我只是不明白为什么它会抛出这个错误。 The "Version" navigation property is defined in the AbstractR configuration and not in the SER configuration (which should also work since SER inherits from AbstractR), am I right ? “Version”导航属性在AbstractR配置中定义,而不是在SER配置中定义(由于SER继承自AbstractR,因此也应该有效),对吗?

Secondly, when removing the Version property & mapping, the same problem appears with the "ChildId" and "ParentId" property used in the SER mapping. 其次,在删除Version属性和映射时,SER映射中使用的“ChildId”和“ParentId”属性会出现同样的问题。 Is this a know problem ? 这是一个已知的问题吗? Am I doing something wrong ? 难道我做错了什么 ?

PS : The ParentId mapping has been removed for simplicity since it seems to be the same problem as the ChildId mapping. PS:为简单起见,已删除ParentId映射,因为它似乎与ChildId映射的问题相同。

Has anyone any idea why this kind of problem is happening ? 有谁知道为什么会出现这种问题?


UPDATE UPDATE

After some more research, it appeared that Fluent API cannot use base class properties for the mapping. 经过一些研究后,似乎Fluent API无法使用基类属性进行映射。 Is that right ? 那正确吗 ? Is this a wanted behavior ? 这是一种通缉行为吗? Why are the DataAnnotations able to use base class properties and not Fluent API ? 为什么DataAnnotations能够使用基类属性而不能使用Fluent API? Aren't all the base class properties inserted inside every classes or is it read with some kind of decorator pattern ? 是不是所有的基类属性都插入到每个类中,或者是否使用某种装饰模式进行读取?

You can use base class properties as foreign key associations as long as the principal also uses navigation properties of type base class. 只要主体还使用类型基类的导航属性,您就可以将基类属性用作外键关联。

The Principal ( Version ) has declared ListOfSER as navigation property of type SER as Dependent Principal( Version )已将ListOfSER声明为SER类型的导航属性为Dependent

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

, but the configuration uses base class property ( VersionId ) as foreign key association ,但配置使用基类属性( VersionId )作为外键关联

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

Which is not allowed when configuring ForeignKeyConstraintConfiguration , take a look at the code excerpt 在配置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);
}

The solution 1 would be changing the ListOfSER type from SER into AbstractR . 解决方案1ListOfSER类型从SER更改为AbstractR

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

This will make more sense, the VersionId is defined on base class, any derived type should be able to use this foreign key association, right? 这样会更有意义, VersionId是在基类上定义的,任何派生类型都应该能够使用这个外键关联,对吧? But unfortunately the Version type only allows SER to be associated with Version . 但不幸的是, Version类型只允许SERVersion相关联。

You also have inconsistency when defining fluent api configuration. 定义流畅的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);
    }
}

Even more, you don't have to configure on both side. 更重要的是,您不必在两侧进行配置。 Just put it either on VersionConfiguration or AbstractRConfiguration would be sufficient. 只需将它放在VersionConfigurationAbstractRConfiguration就足够了。

The solution 2 would be moving the Version and VersionId from base class to SER , if you want Version only to be associated with SER . 解决方案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; }
}

And configuring either on Version configuration or SER configuration. 并在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);
    }
}

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

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