简体   繁体   English

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

[英]EF 6 Code-First Many to Many relationship with same entity on non-primary key

I have an EF 6 Task entity, which looks like this: 我有一个EF 6任务实体,如下所示:

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;}
}

Tasks of a certain type can be dependent on all other tasks of a certain type to finish running before they run, and those dependencies should be defined in a table like this: 某种类型的任务可以依赖于某种类型的所有其他任务在运行之前完成运行,这些依赖关系应在如下表中定义:

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

Example of what I want- Task A has Type 1, Task B and C have Type 2. I have a TaskTypeDependency of 我想要的示例-任务A具有类型1,任务B和C具有类型2。我具有TaskTypeDependency为

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

At runtime, I want to get all the tasks that task is dependent on, which for Task A would be Task B and Task C, to check if the dependent tasks have completed. 在运行时,我想获取任务所依赖的所有任务(对于任务A而言,将是任务B和任务C),以检查相关任务是否已完成。 Is there a way to set this relationship up in Code-First, maybe with Fluent API? 是否可以使用Fluent API在Code-First中设置这种关系? Or am I stuck using LINQ to sort this all out without a virtual property? 还是我坚持使用LINQ在没有虚拟属性的情况下解决所有问题?

Welcome to StackOverflow! 欢迎来到StackOverflow!

As far as I know, EF cannot handle relationships of this kind. 据我所知,EF无法处理这种关系。

Your problem isn't that the principal key isn't the primary key. 您的问题不是主键不是主键。 Your main problem is that those keys ( TaskType s) are not unique . 您的主要问题是这些键( TaskType不是唯一的

Solution proposal 解决方案建议

You can work around this limitation by encapsulating the logic of loading the dependent tasks in a repository. 您可以通过将加载相关任务的逻辑封装在存储库中来解决此限制。 While many people advice against building a repository pattern on top of EF, and most of the times I'm on that side too, this is a great example of why it can be useful. 尽管许多人建议不要在EF之上构建存储库模式,而且大多数时候我也支持该模式,但这是一个很好的例子,说明了它为什么有用的原因。

I would create a method that populates dependencies using LINQ, maybe an extension method, something along the lines of: 我将创建一种使用LINQ填充依赖项的方法,也许是一种扩展方法,大致类似于:

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

Then in your repository you can use this method on the Tasks you loaded before you return them. 然后,在存储库中,可以在加载的任务上使用此方法,然后再返回它们。 Eg: 例如:

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

Then when your business logic calls your repository, it will receive objects that already have their Dependencies property filled. 然后,当您的业务逻辑调用存储库时,它将接收已经填充了Dependencies属性的对象。

There is one optimization that is worth mentioning here, though. 不过,这里有一个优化值得一提。 It isn't as important for getting a single task, but makes a lot of difference when loading all of them. 它对于获得单个任务并不重要,但是在加载所有任务时会产生很大的不同。 If you implement the GetAll method naively, as follows, it will retrieve the list of Tasks from the server n times , which is not great. 如果您天真地实现GetAll方法,如下所示, 它将从服务器检索n次Tasks列表 ,这不是很好。

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

You should pass the already existing allTasks variable to the method instead. 您应该将已经存在的allTasks变量传递给该方法。 If - in another scenario - you don't have a list of tasks already, another solution is to call _context.Tasks.Load() , and use _context.Tasks.Load thereafter. 如果-在另一种情况下-您还没有任务列表,则另一种解决方案是调用_context.Tasks.Load() ,然后使用_context.Tasks.Load

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM