实体框架6具有附加导航属性的一对多

时间:2019-01-10 19:28:52

标签: entity-framework entity-framework-6

我正在尝试映射一个班级,在那里我有一个相关项目的列表和一个选定的相关项目。基本上,我有一个包含任务集合的工作流,并且在任何给定时间,这些任务之一都被选择为当前任务。

public class Flow
{
    public int FlowId { get; set; }
    public int CurrentFlowTaskId { get; set; }
    public bool IsActive { get; set; }

    public virtual FlowTask CurrentFlowTask { get; set; }
    public virtual ICollection<FlowTask> FlowTasks { get; set; }
}

public class FlowTask
{
    public int FlowTaskId { get; set; }
    public int FlowId { get; set; }
    public string Discription { get; set; }
    public virtual Flow Flow { get; set; }
}

我的映射如下:

public class FlowMap : EntityTypeConfiguration<Flow>
{
    public FlowMap()
    {
        HasKey(x => x.FlowId);
        Property(x => x.IsActive).IsRequired();

        HasOptional(x => x.CurrentFlowTask)
            .WithOptionalPrincipal()
            .WillCascadeOnDelete(false);

        HasMany(x => x.FlowTasks)
            .WithRequired(x => x.Flow)
            .HasForeignKey(x => x.FlowId)
            .WillCascadeOnDelete(false);
    }
}

public class FlowTaskMap : EntityTypeConfiguration<FlowTask>
{
    public FlowTaskMap()
    {
        HasKey(x => x.FlowTaskId);
        Property(x => x.Discription).HasMaxLength(25).IsRequired();
    }
}

这将创建如下所示的迁移:

CreateTable(
    "dbo.Flows",
    c => new
        {
            FlowId = c.Int(nullable: false, identity: true),
            CurrentFlowTaskId = c.Int(nullable: false),
            IsActive = c.Boolean(nullable: false),
        })
    .PrimaryKey(t => t.FlowId);

CreateTable(
    "dbo.FlowTasks",
    c => new
        {
            FlowTaskId = c.Int(nullable: false, identity: true),
            FlowId = c.Int(nullable: false),
            Discription = c.String(nullable: false, maxLength: 25),
            Flow_FlowId = c.Int(),
        })
    .PrimaryKey(t => t.FlowTaskId)
    .ForeignKey("dbo.Flows", t => t.Flow_FlowId)
    .ForeignKey("dbo.Flows", t => t.FlowId)
    .Index(t => t.FlowId)
    .Index(t => t.Flow_FlowId);

这里似乎不对的第一件事是在流程任务中创建的Flow_FlowId列。

当我在LinqPad中运行以下代码块时,看不到预期的结果;创建了FlowFlowTask,但是Flow.CurrentTaskId为空,并且关闭的Flow_FlowId列设置为与Flow.FlowId

相同的值。
var fi = new CompWalk.Data.FlowTask
{
    Discription = "Task 1",
};
var f = new CompWalk.Data.Flow {
    IsActive = true,
    CurrentFlowTask = fi,
    FlowTasks = new[] {
        fi
    }
};

Flows.Add(f);
SaveChanges();

此代码改编自几乎相同的问题here,但已有数年历史,因此可能不再适用。

在不进行多次插入和保存的情况下我能尝试的是什么? 另外,是什么原因导致生成Flow_FlowId列?

1 个答案:

答案 0 :(得分:1)

FlowFlowTask之间有两种不同的关系类型。您通过向FlowId添加FlowTask来配置了一对多关系,并对其进行了配置,以便我们在迁移中使用此行

.ForeignKey("dbo.Flows", t => t.FlowId)

这是正确的。并且存在一对一的关系,其中您将Flow标记为主体,因此FlowTask是依赖的。实体框架将主体ID作为外键添加到从属实体以创建这种关系。在实体框架中,无法为与实体属性的一对一关系配置外键(就像您为一对多关系那样配置)。并且由于该框架,框架会为您生成外键并将其添加到从属实体(FlowTask)。

.ForeignKey("dbo.Flows", t => t.Flow_FlowId)

您的public int CurrentFlowTaskId { get; set; }属性只是一个常规列,而不是外键,因此在您的示例中实际上并未设置。并且Flow_FlowId被设置为Flow.FlowId,因为它外键。

如果要将一对一关系外键添加到Flow,只需更改此行

HasOptional(x => x.CurrentFlowTask)
    .WithOptionalPrincipal()
    .WillCascadeOnDelete(false);

HasOptional(x => x.CurrentFlowTask)
    .WithOptionalDependent()
    .WillCascadeOnDelete(false);

如果要指定密钥名称,请将其更改为以下名称

HasOptional(x => x.CurrentFlowTask)
    .WithOptionalDependent()
    .Map(m => m.MapKey("KeyName"))
    .WillCascadeOnDelete(false);

但是,如果您决定向KeyName实体添加Flow属性,则迁移将产生异常:

KeyName: Name: Each property name in a type must be unique. Property name 'KeyName' is already defined.

因此您将无法直接在代码中访问此值,并且我不知道如何解决此问题。尚未研究。但是我认为应该可以将这种关系配置为一对多,并以某种方式将其使用为一对一,但是我不确定。