如何打破GetOrCreate系列方法的递归循环?
解决Employee/Position/Division创建循环依赖问题
问题背景
创建Division时,若未找到对应Director会调用GetOrCreateEmployee创建员工;创建Employee必须关联Position;创建Position又必须依赖Division,三个GetOrCreate方法互相调用形成递归死循环,尝试过标记和方法重载未解决。
原始代码
static public Employee GetOrCreateEmployee() { Console.WriteLine("Enter the full name of the teacher:"); string employeeName = Console.ReadLine(); Employee employee = DB.employees.FirstOrDefault(e => $"{e.Name} {e.Surname} {e.Patronymic}".ToLower() == employeeName.ToLower()); if (employee == null) { Console.WriteLine("Enter the employee's name:"); string name = Console.ReadLine(); Console.WriteLine("Enter the employee's last name:"); string surname = Console.ReadLine(); Console.WriteLine("Enter the employee's middle name:"); string patronymic = Console.ReadLine(); Position position = GetOrCreatePosition(); employee = new Employee(name, surname, patronymic, position); DB.employees.Add(employee); } return employee; } static public Position GetOrCreatePosition() { Console.WriteLine("Enter the title of the position:"); string jobTitleName = Console.ReadLine(); Position position = DB.positions.FirstOrDefault(j => j.Title.ToLower() == jobTitleName.ToLower()); if (position == null) { position = DB.positions.FirstOrDefault(j => j.Title.ToLower() == jobTitleName.ToLower()); } Console.WriteLine("Enter the title of the position:"); string name = Console.ReadLine(); Console.WriteLine("Enter the salary of the position:"); int.TryParse(Console.ReadLine(), out int salary); Console.WriteLine("Enter the name of the department:"); string subdivisionName = Console.ReadLine(); Division division = DB.divisions.FirstOrDefault(s => s.Name.ToLower() == subdivisionName.ToLower()); if (division == null) { Console.WriteLine("The division was not found. Create it"); GetOrCreateDivision(); } division = DB.divisions.FirstOrDefault(s => s.Name.ToLower() == subdivisionName.ToLower()); Position speciality = new Position(name, salary, division); DB.positions.Add(speciality); Console.WriteLine("The position has been successfully created."); return position; } static public void GetOrCreateDivision() { Console.WriteLine("Enter the name of the department:"); string name = Console.ReadLine(); Console.WriteLine("Enter the last name, first name and patronymic of the director:"); string directorFullName = Console.ReadLine(); Employee director = DB.employees.FirstOrDefault(e => $"{e.Name} {e.Surname} {e.Patronymic}" == directorFullName); if (director == null) { Console.WriteLine("The director has not been found. Create it."); GetOrCreateEmployee(); director = DB.employees.Last(); } Console.WriteLine("Enter the name of the organization:"); string organizationName = Console.ReadLine(); Organisation organization = DB.organisations.FirstOrDefault(o => o.Name.ToLower() == organizationName.ToLower()); if (organization == null) { Console.WriteLine("The organization was not found. Create it"); GetOrCreateOrganization(); } organization = DB.organisations.FirstOrDefault(o => o.Name.ToLower() == organizationName.ToLower()); Division subdivision = new Division(name, director, organization); DB.divisions.Add(subdivision); Console.WriteLine("The division has been successfully created."); }
解决方案
循环的核心是实体间的强制依赖导致创建顺序死锁,以下三种方案可解决问题:
方案1:拆分实体初始化流程,允许临时无依赖实体
修改实体类,添加仅包含基础字段的构造函数,先创建核心实体,后续补全关联关系:
- 给实体添加精简构造函数:
// Employee允许先创建无职位的实例 public Employee(string name, string surname, string patronymic) { Name = name; Surname = surname; Patronymic = patronymic; } // Position允许先创建无部门的实例 public Position(string title, int salary) { Title = title; Salary = salary; } // Division允许先创建无负责人的实例 public Division(string name, Organisation organization) { Name = name; Organisation = organization; } - 调整
GetOrCreate方法逻辑:- 创建Director时,先存入无Position的Employee
- 创建Division时,关联已创建的Director
- 创建Position时,关联已创建的Division
- 最后统一给Director补全Position信息
方案2:给方法添加重载,跳过强制依赖
通过重载方法,允许在特定场景下跳过循环依赖的创建步骤:
- 修改
GetOrCreateEmployee添加重载:static public Employee GetOrCreateEmployee(bool requirePosition = true) { Console.WriteLine("Enter the full name of the teacher:"); string employeeName = Console.ReadLine(); Employee employee = DB.employees.FirstOrDefault(e => $"{e.Name} {e.Surname} {e.Patronymic}".ToLower() == employeeName.ToLower()); if (employee == null) { Console.WriteLine("Enter the employee's name:"); string name = Console.ReadLine(); Console.WriteLine("Enter the employee's last name:"); string surname = Console.ReadLine(); Console.WriteLine("Enter the employee's middle name:"); string patronymic = Console.ReadLine(); if (requirePosition) { Position position = GetOrCreatePosition(); employee = new Employee(name, surname, patronymic, position); } else { // 创建无职位的Director,后续补充 employee = new Employee(name, surname, patronymic); } DB.employees.Add(employee); } return employee; } - 修改
GetOrCreateDivision调用重载:static public void GetOrCreateDivision() { // ... 原有输入部门名称、组织的逻辑 ... if (director == null) { Console.WriteLine("The director has not been found. Create it."); // 创建无职位的Director,避免触发循环 director = GetOrCreateEmployee(requirePosition: false); } // ... 创建Division的逻辑 ... // 后续补全Director的职位 Console.WriteLine("Do you want to set a position for the director? (Y/N)"); if (Console.ReadLine().Trim().ToLower() == "y") { Position directorPosition = GetOrCreatePosition(); director.Position = directorPosition; // 根据你的DB实现更新Employee记录 } }
方案3:重构业务依赖,从根源打破循环
重新梳理业务逻辑,解除不必要的强制依赖:
- 允许员工入职时暂不分配职位,后续再设置
- 允许部门先创建,后续再指定负责人
从业务规则层面移除循环依赖的强制要求,彻底避免死循环。
代码冗余修正
注意GetOrCreatePosition中有重复无效的查询代码,需删除:
// 这段代码完全冗余,删除 if (position == null) { position = DB.positions.FirstOrDefault(j => j.Title.ToLower() == jobTitleName.ToLower()); }
内容的提问来源于stack exchange,提问作者Versst
相关产品推荐
相关产品推荐

