Entity framework 实体框架:一对零或与主体上的外键的一种关系

Entity framework 实体框架:一对零或与主体上的外键的一种关系,entity-framework,ef-code-first,entity-framework-6,entity-framework-migrations,Entity Framework,Ef Code First,Entity Framework 6,Entity Framework Migrations,我有一个1:0..1的关系,我想使用fluent API与EF 6进行映射。该关系由一个主体组成,主体可以有也可以没有从属关系。依赖者必须始终有一个主体 在principal中,我需要访问受抚养人的Id 我的代码如下所示: public class Principal { public int Id {get; private set; } public int? DependentId { get; private set; } public virtual Depe

我有一个1:0..1的关系,我想使用fluent API与EF 6进行映射。该关系由一个主体组成,主体可以有也可以没有从属关系。依赖者必须始终有一个主体

在principal中,我需要访问受抚养人的Id

我的代码如下所示:

public class Principal
{
    public int Id {get; private set; }

    public int? DependentId { get; private set; }
    public virtual Dependent Dependent { get; private set; }
}

public class Dependent
{
    public int Id { get; private set; }

    public virtual Principal Principal { get; private set; }
}
    public class PrincipalMap : EntityTypeConfiguration<Principal>
    {
        public PrincipalMap()
        {
            ToTable("PRINCIPALS");

            HasKey(x => x.Id);

            Property(x => x.Id)
                .HasColumnName("PRINCIPALID")
                .IsRequired()
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            Property(x => x.DependentId)
                .HasColumnName("DEPENDENTID")
                .IsOptional();
        }
    }

    public class DependentMap : EntityTypeConfiguration<Dependent>
    {
        public DependentMap()
        {
            ToTable("DEPENDENTS");

            HasKey(x => x.Id);

            Property(x => x.Id)
                .HasColumnName("DEPENDENTID")
                .IsRequired()
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            HasRequired(x => x.Principal).WithOptional(x => x.Dependent).Map(x => x.MapKey("PRINCIPALID")).WillCascadeOnDelete();
        }
    }
我的映射如下所示:

public class Principal
{
    public int Id {get; private set; }

    public int? DependentId { get; private set; }
    public virtual Dependent Dependent { get; private set; }
}

public class Dependent
{
    public int Id { get; private set; }

    public virtual Principal Principal { get; private set; }
}
    public class PrincipalMap : EntityTypeConfiguration<Principal>
    {
        public PrincipalMap()
        {
            ToTable("PRINCIPALS");

            HasKey(x => x.Id);

            Property(x => x.Id)
                .HasColumnName("PRINCIPALID")
                .IsRequired()
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            Property(x => x.DependentId)
                .HasColumnName("DEPENDENTID")
                .IsOptional();
        }
    }

    public class DependentMap : EntityTypeConfiguration<Dependent>
    {
        public DependentMap()
        {
            ToTable("DEPENDENTS");

            HasKey(x => x.Id);

            Property(x => x.Id)
                .HasColumnName("DEPENDENTID")
                .IsRequired()
                .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

            HasRequired(x => x.Principal).WithOptional(x => x.Dependent).Map(x => x.MapKey("PRINCIPALID")).WillCascadeOnDelete();
        }
    }
如您所见,列
DEPENDENTID
不是外键。运行程序并将从属对象与主体关联时,DependentId属性保持为空,即EF无法识别它与从属对象本身相关


我做错了什么?

是的,这很棘手,而且在我看来是一个EF错误。我使用的解决方法是伪1:M:

HasRequired(x => x.Principal)
  .WithMany()
  .HasForeignKey(x => x.DependentId);
  .WillCascadeOnDelete();

在DependentMap中,您声明字段DEPENDENTID为数据库生成(标识)的依赖表的主键,因此它永远不会是外键。您不能随意更改它(使其指向您选择的实体)

此外,对于EF(和E/R),不需要两列(每个表一列)就可以建立1-0..1关系。只能有一列(不可为空)

在您的情况下,此模型应适用于:

public class Principal
{
    public int Id { get; private set; }

    public virtual Dependent Dependent { get; private set; }
}

public class Dependent
{
    public int Id { get; private set; }

    public virtual Principal Principal { get; private set; }
}

public class PrincipalMap : EntityTypeConfiguration<Principal>
{
    public PrincipalMap()
    {
        ToTable("PRINCIPALS");

        HasKey(x => x.Id);

        Property(x => x.Id)
            .HasColumnName("PRINCIPALID")
            .IsRequired()
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

public class DependentMap : EntityTypeConfiguration<Dependent>
{
    public DependentMap()
    {
        ToTable("DEPENDENTS");

        HasKey(x => x.Id);

        Property(x => x.Id)
            .HasColumnName("DEPENDENTID")
            .IsRequired()
            .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);

        HasRequired(x => x.Principal).WithOptional(x => x.Dependent).Map(x => x.MapKey("PRINCIPALID")).WillCascadeOnDelete();
    }
}
(我省略了级联删除,但也应该清楚)

E/R模型为正常形式(并且是唯一适用于EF的模型)。
顺便说一句,如果访问
Principal.Dependent
property,EF将生成一个类似于
selected*from Dependent的查询,其中PRINCIPALID=
where是主体实体的id,因此它真正起作用

现在,关于您的需求,要从主体访问Dependent.Id,唯一的方法是
dependentId=Principal.Dependent.Id
(或者更好的方法是
dependentId=Principal.Dependent==null?null:Principal.Dependent.Id

如果您真的想为主体上引用依赖表的外键创建一个字段,该怎么办?
此模型不是正常形式,因此EF将不会处理它(对于DBMS,您也需要编写触发器来处理它)。
我的意思是,在R-DBMS中没有一个约束,您可以指定如果列DEPENDENT.PRINCIPALID引用主体,那么列PRINCIPAL.DEPENDENTID也应该引用原始的依赖项。

在这种情况下,您需要自己处理PRINCIPAL.DEPENDENTID(即主体实体必须具有DEPENDENTID属性,您必须自己处理,并且EF在导航期间不使用该属性)

尝试仅在一个配置文件中设置
主体
/
依赖关系。@krillgar我已经尝试过了,运气不好。你能给我一个代码示例吗?如果主体被配置为“有许多”依赖项,主体怎么能持有依赖项的外键?另外,代码没有编译:
.HasForeignKey(x=>x.DependentId)
需要
Dependent
类的属性,但是
DependentId
显然是
主体
类的属性感谢您非常清楚的回答。我希望主体上有依赖id的真正原因是,每次选择主体时,我都需要知道是否存在依赖id。因为这些选择是频繁的,我想让它们保持快速。因此我认为最好将
DependentId
作为
Principal
上的字段,而不是
Principal.Dependent.Id
,因为后者需要一个连接。当然,我给出的例子是简化的,实际上我有许多不同的依赖属性(每个属性都有自己的表),对于每个属性,我需要知道它是否存在。我理解这个问题。在这种情况下,您需要自己以某种方式(即在数据库上使用触发器并将列设置为数据库生成的列、覆盖savechanges或使用其他技术)来处理DependentId。我不知道您的要求,但您也可以评估在主体表中插入所有字段和/或使用ComplexType(类似于使用更干净的模型在主体表中插入所有字段)和/或使用继承(通常1-1关系与继承相关;如果使用TPH,结果类似于插入主体表中的所有字段)。