我有一个1:0..1的关系,我想用EF 6使用流畅的API进行映射。该关系由一个委托人组成,该委托人可能有也可能没有受抚养人。依赖者必须始终拥有委托人。
在校长中,我需要访问依赖的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();
}
}
导致以下迁移:
CreateTable(
"dbo.PRINCIPALS",
c => new
{
PRINCIPALID = c.Int(nullable: false, identity: true),
DEPENDENTID = c.Int(),
})
.PrimaryKey(t => t.PRINCIPALID);
CreateTable(
"dbo.DEPENDENTS",
c => new
{
DEPENDENTID = c.Int(nullable: false, identity: true),
PRINCIPALID = c.Int(nullable: false),
})
.PrimaryKey(t => t.DEPENDENTID)
.ForeignKey("dbo.PRINCIPALS", t => t.PRINCIPALID, cascadeDelete: true)
.Index(t => t.PRINCIPALID);
如您所见,列DEPENDENTID
不是外键。运行程序并将依赖对象与主体关联时, DependentId
属性保持为空,即EF无法识别它与依赖本身相关。
我做错了什么?
答案 0 :(得分:1)
在DependentMap中,您将字段DEPENDENTID声明为DEPENDENT表的主键,数据库生成(标识),因此它永远不会是外键。您无法根据需要更改它(使其指向您选择的实体)。
此外,使用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();
}
}
在这种情况下,表创建stataments(由EF提供者生成)应该类似于
ExecuteNonQuery==========
CREATE TABLE [DEPENDENTS] (
[DEPENDENTID] int not null identity(1,1)
, [PRINCIPALID] int not null
);
ALTER TABLE [DEPENDENTS] ADD CONSTRAINT [PK_DEPENDENTS_204c4d57] PRIMARY KEY ([DEPENDENTID])
ExecuteNonQuery==========
CREATE TABLE [PRINCIPALS] (
[PRINCIPALID] int not null identity(1,1)
);
ALTER TABLE [PRINCIPALS] ADD CONSTRAINT [PK_PRINCIPALS_204c4d57] PRIMARY KEY ([PRINCIPALID])
ExecuteNonQuery==========
CREATE INDEX [IX_PRINCIPALID] ON [DEPENDENTS] ([PRINCIPALID])
ExecuteNonQuery==========
ALTER TABLE [DEPENDENTS] ADD CONSTRAINT [FK_DEPENDENTS_PRINCIPALS_PRINCIPALID] FOREIGN KEY ([PRINCIPALID]) REFERENCES [PRINCIPALS] ([PRINCIPALID])
(我在级联删除时省略,但也应该清楚)。
E / R模型处于正常状态(并且是唯一适用于EF的模型)
顺便说一句,如果您访问Principal.Dependent
属性,EF将生成类似于selected * from dependent where PRINCIPALID = <principal_id>
的查询,其中是主体实体的ID,因此它确实有效。
现在,关于您的要求,要从Principal访问Dependent.Id,唯一的方法是dependentId = Principal.Dependent.Id
(或者更好,dependentId = Principal.Dependent == null ? null : Principal.Dependent.Id
)。
如果您真的想要 PRINCIPAL上涉及DEPENDENT表的外键字段,该怎么办?
此模型不是正常形式,因此EF不会处理它(也需要DBMS,你需要编写触发器来处理它)
我的意思是,在R-DBMS中没有约束,您可以指定如果列DEPENDENT.PRINCIPALID引用PRINCIPAL,则列PRINCIPAL.DEPENDENTID应引用原始DEPENDENT。
在这种情况下,您需要做的是自己处理PRINCIPAL.DEPENDENTID(即,Principal实体必须具有您必须自己处理的DEPENDENTID属性,并且在导航期间不会被EF使用)。
答案 1 :(得分:0)
是的,这很棘手且是一个EF错误的IMO。我使用的解决方法是伪1:M:
HasRequired(x => x.Principal)
.WithMany()
.HasForeignKey(x => x.DependentId);
.WillCascadeOnDelete();