EF 6非主键上与同一实体的代码优先多对多关系

时间:2019-02-13 16:59:15

标签: c# .net database entity-framework entity-framework-6

我有一个EF 6 Task实体,看起来像这样:

public class Task
{
    public Guid TaskId {get; set;}
    public TaskTypeEnum TaskType {get; set;}
    public TaskStatusEnum TaskStatus {get; set;}

    public virtual ICollection<Task> Dependencies {get; set;}
}

某种类型的任务可以依赖于某种其他类型的其他任务才能在运行之前完成运行,这些依赖关系应在如下表中定义:

public class TaskTypeDependency
{
    public TaskTypeEnum TaskType {get; set;}
    public TaskTypeEnum DependsOnTaskType {get; set;}
}

我想要的示例-任务A具有类型1,任务B和C具有类型2。我具有TaskTypeDependency为

TaskType | DependsOnTaskType
----------------------------
1        |  2              

在运行时,我想获取该任务所依赖的所有任务,对于任务A而言,将是任务B和任务C,以检查相关任务是否已完成。是否可以使用Fluent API在Code-First中设置这种关系?还是我坚持使用LINQ在没有虚拟属性的情况下解决所有问题?

1 个答案:

答案 0 :(得分:0)

欢迎使用StackOverflow!

据我所知,EF无法处理这种关系。

您的问题不是主键不是主键。您的主要问题是这些键(TaskType s 不是唯一的

解决方案

您可以通过封装将依赖任务加载到存储库中的逻辑来解决此限制。尽管许多人advice against在EF之上构建存储库模式,但在大多数情况下,我也在这一方面,但这是一个很好的例子,说明了它为什么有用的原因。

我将创建一种使用LINQ填充依赖项的方法,也许是一种扩展方法,类似于:

private static void LoadDependentTasks(this Task task, IEnumerable<Task> allTasks){
    task.Dependencies = allTasks.Where(yourCustomSelector).ToList();
}

然后,您可以在资源库中对加载的任务使用此方法,然后再将其返回。例如:

public Task GetById(Guid taskId){
    Task t = _context.Tasks.Find(taskId);
    t.LoadDependentTasks(_context.Tasks);
}

然后,当您的业务逻辑调用存储库时,它将接收已经填充了其Dependencies属性的对象。

这里有一种优化,值得一提。它对于获得单个任务并不重要,但是在加载所有任务时会产生很大的不同。如果您天真地实现GetAll方法,如下所示,它将从服务器中 n 检索任务列表,这不是很好。

public Task GetAll(){
    List<Task> allTasks = _context.Tasks.ToList();
    foreach(var task in allTasks)
        task.LoadDependentTasks(_context.Tasks);
    return allTasks;
}

您应该将已经存在的allTasks变量传递给方法。如果-在另一种情况下-您还没有任务列表,则另一种解决方案是调用_context.Tasks.Load(),然后使用_context.Tasks.Load