递归 linq 表达式以获取非 NULL 父值?
Posted
技术标签:
【中文标题】递归 linq 表达式以获取非 NULL 父值?【英文标题】:Recursive linq expressions to get non NULL parent value? 【发布时间】:2022-01-13 19:22:31 【问题描述】:我编写了一个简单的递归函数来爬上具有 ID 和 PARENTID 的表的树。
但是当我这样做时,我得到了这个错误
System.InvalidOperationException:“无法跟踪实体类型“InternalOrg”的实例,因为已经在跟踪具有相同键值 'Id' 的另一个实例。附加现有实体时,请确保仅附加一个具有给定键值的实体实例。
是否有另一种方法可以做到这一点,或者可以在一个 LINQ 表达式中完成?
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
if (dcDto.InternalOrgId != null)
InternalOrg io = _internalOrgRepo.Get(Convert.ToInt32(dcDto.InternalOrgId));
InternalOrgDto ioDto = new InternalOrgDto
Id = io.Id,
Abbreviation = io.Abbreviation,
Code = io.Code,
Description = io.Description
;
return ioDto;
else
//manually get parent department
Department parentDepartment = _departmentRepo.Get(Convert.ToInt32(dcDto.ParentDepartmentId));
DepartmentChildDto parentDepartmenDto = ObjectMapper.Map<DepartmentChildDto>(parentDepartment);
return GetInternalOrgDto(parentDepartmenDto);
【问题讨论】:
显示调用堆栈。 Bug 绝对不在这个函数中。这可能是由Attach
、Update
、Add
引起的。
递归调用 .get() 的底层函数是原因。下面的解决方案有效
【参考方案1】:
有没有办法通过 Linq 从给定的孩子那里获得***父母?不是我知道的。您可以像您所做的那样递归地执行此操作,但我建议简化查询以避免加载整个实体,直到您获得所需的内容。我从您的代码中猜测只有***父部门才有 InternalOrg?否则,此方法将递归父母,直到找到一个。这可以加快速度:
private InternalOrgDto GetInternalOrgDto(DepartmentChildDto dcDto)
var internalOrgid = dcDto.InternalOrgId
?? FindInternalOrgid(dcDto.ParentDepartmentId)
?? throw new InternalOrgNotFoundException();
InternalOrgDto ioDto = _context.InternalOrganizations
.Where(x => x.InternalOrgId == internalOrgId.Value)
.Select(x => new InternalOrgDto
Id = x.Id,
Abbreviation = x.Abbreviation,
Code = x.Code,
Description = x.Description
).Single();
return ioDto;
private int? FindInternalOrgid(int? departmentId)
if (!departmentId.HasValue)
return (int?) null;
var details = _context.Departments
.Where(x => x.DepartmentId == departmentId.Value)
.Select(x => new
x.InternalOrgId,
x.ParentDepartmentId
).Single();
if (details.InternalOrgId.HasValue)
return details.InternalOrgId;
return findInternalOrgId(details.parentDepartmentId);
这里的主要考虑因素是避免返回实体或实体集的存储库方法,尤其是在您不需要关于实体的所有信息的情况下。通过利用 EF 通过 Linq 提供的 IQueryable
,我们可以仅投影到我们需要的数据,而不是返回每个字段。数据库服务器可以通过索引更好地适应这一点,并帮助避免锁定之类的事情。如果您使用存储库来强制执行低级域规则或启用单元测试,则存储库可以公开 IQueryable<TEntity>
而不是 IEnumerable<TEntity>
甚至 TEntity
以启用投影和其他 EF Linq 优点。
另一个选项考虑我在哪里有层次数据,其中关系很重要,我想快速找到与父级相关的所有实体,或者到达特定级别,一个选项是存储每条更新记录的面包屑如果该项目曾经被移动过。好处是这些检查变得非常简单,风险是任何地方/任何可以修改数据关系的东西都可能使面包屑路径处于无效状态。
例如,如果我的部门 ID 22 属于部门 8,而部门 2 属于***部门,则 22 的面包屑路径将是:“2,8”。如果面包屑为空,则我们有一个***实体。 (并且没有父 ID)我们可以使用简单的string.Split()
操作来解析面包屑。这完全避免了对 DB 的递归访问。尽管您可能希望在后台运行维护工作,以定期检查最近修改的数据,以确保其面包屑跟踪准确,并在有任何损坏时提醒您。 (通过错误代码等)
【讨论】:
以上是关于递归 linq 表达式以获取非 NULL 父值?的主要内容,如果未能解决你的问题,请参考以下文章