Entity Framework 6 один ко многим с дополнительным свойством навигации - PullRequest
0 голосов
/ 10 января 2019

Я пытаюсь отобразить класс, в котором у меня есть список связанных элементов и выбранный связанный элемент. По сути, у меня есть рабочий процесс с набором задач, и в любой момент времени одна из этих задач выбирается в качестве текущей задачи.

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, я не вижу ожидаемых результатов; Flow и FlowTask созданы, но Flow.CurrentTaskId имеет значение NULL, а для столбца off 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();

Этот код был адаптирован из почти идентичного вопроса здесь , но ему уже несколько лет, поэтому он может быть неприменим.

Возможно ли то, что я пытаюсь сделать, не делая несколько вставок и сохранений? Кроме того, что вызывает генерацию столбца Flow_FlowId?

1 Ответ

0 голосов
/ 23 января 2019

У вас есть 2 разных типа отношений между Flow и FlowTask. Вы настроили отношение один-ко-многим, добавив FlowId к FlowTask и настроили его, чтобы у нас была эта строка в миграции

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

что правильно. И есть отношения один-к-одному, где вы отметили Flow как принципал и, следовательно, FlowTask зависит. Entity Framework добавляет основной идентификатор в качестве внешнего ключа для зависимого объекта для создания таких отношений. Невозможно настроить внешний ключ для отношения «один к одному» со свойством объекта (как вы это сделали для «один-ко-многим») в Entity Framework. И поэтому фреймворк генерирует для вас внешний ключ и добавляет его к зависимому объекту (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.

Так что вы не сможете получить доступ к этому значению напрямую в коде, и я не знаю, как это исправить. Еще не исследовал это. Но я думаю, что должна быть возможность настроить это отношение как один-ко-многим и как-то использовать его как один-к-одному, но я не уверен.

...