gpt4 book ai didi

c# - EF Code First 提高自引用性能,一对多关系

转载 作者:太空狗 更新时间:2023-10-29 23:52:14 24 4
gpt4 key购买 nike

我有一个 AccountGroup,它是一个自引用实体。叶 AccountGroup 可以包含 1 个或多个 Accounts。两个实体都有 Balance 属性。每个 AccountGroup 都有一个 Balance,它可以是子组中 Balance 的总和,也可以是 Balance 的总和所有帐户(在叶组的情况下)。

为了构建所有 AccountGroupAccount 的树状列表,我必须递归地遍历这个对象图,这导致了很多(我的意思是很多! !!) 对数据库的调用...

有没有什么方法可以通过减少数据库调用次数来改进这一点?

谢谢

这是精简后的代码

账户(仅属于 1 个账户组)

public class Account
{
public int Id { get; set; }
public int GroupId { get; set; }
public string Name { get; set; }
public decimal Balance { get; set; }
public string AccountType { get; set; }

public virtual AccountGroup Group { get; set; }
}

AccountGroup(有0个或多个AccountGroup,如果是叶子则有1个或多个Accounts)

public class AccountGroup
{
public AccountGroup()
{
Accounts = new HashSet<Account>();
Groups = new HashSet<AccountGroup>();
}

public int Id { get; set; }
public bool IsRoot { get { return Parent == null; } }
public bool IsLeaf { get { return !Groups.Any(); } }
public decimal Balance { get { return IsLeaf ? Accounts.Sum(a => a.Balance) : Groups.Sum(g => g.Balance); } } // if leaf group, get sum of all account balances, otherwise get sum of all subgroups
public int? ParentId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public virtual ISet<Account> Accounts { get; private set; }
public virtual ISet<AccountGroup> Groups { get; private set; }
public virtual AccountGroup Parent { get; set; }
}

调用代码

// start processing root groups (ones without parent)
foreach (var rootGroup in db.AccountGroups.Include(g=>g.Groups).Where(g => g.ParentId == null))
{
TraverseAccountGroup(rootGroup, 0);
}

// recursive method
private static void TraverseAccountGroup(AccountGroup accountGroup, int level)
{
//
// process account group
//
Console.WriteLine("{0}{1} ({2})", String.Empty.PadRight(level * 2, '.'), accountGroup.Name, level);
//
// if subgroups exist, process recursivelly
//
if (accountGroup.Groups.Any())
{
foreach (var subGroup in accountGroup.Groups)
{
TraverseAccountGroup(subGroup, level + 1);
}
}
//
// otherwise, process accounts belonging to leaf subgroup
//
else
{
foreach (var account in accountGroup.Accounts)
{
Console.WriteLine("ACCOUNT [{0}]", account.Name);
}
}
}

最佳答案

CTE 方法

有两种方法可以提高对树数据类型的查询速度。第一个(可能也是最简单的)是使用存储过程和 EF 的执行 sql 功能来加载树。 SProc 会缓存,结果集的执行速度会提高。我对存储过程中查询的建议是递归 CTE。

http://msdn.microsoft.com/en-us/library/ms186243(v=sql.105).aspx

with <CTEName> as
(
SELECT
<Root Query>
FROM <TABLE>

UNION ALL

SELECT
<Child Query>
FROM <TABLE>
INNER JOIN <CTEName>
ON <CTEJoinCondition>
WHERE
<TERMINATION CONDITION>

)

编辑

执行您的 sproc 或 CTE 内联:

DbContext ctx = new SampleContext();
ctx.Database.SqlQuery<YourEntityType>(@"SQL OR SPROC COMMAND HERE", new[] { "Param1", "Param2", "Etc" });

展平你的树结构

第二种方法是构建树的平面表示。您可以将树扁平化为扁平结构以便快速查询,然后使用扁平结构与实际树节点之间的链接来切出自引用实体。您可以使用上述递归 CTE 查询构建平面结构。

这只是一种方法,但是有很多关于这个主题的论文:

http://www.governor.co.uk/news-plus-views/2010/5/17/depth-first-tree-flattening-with-the-yield-keyword-in-c-sharp/

编辑:添加额外说明请注意,递归 CTE 缓存是在遍历结构之前查询的符号。这是编写查询来解决您的问题的最快和最简单的方法。但是,这必须是 SQL 查询。您可以直接使用 execute sql,也可以执行 SProc。 Sprocs 在运行后缓存执行图,因此它们比必须在运行前构建执行计划的 native 查询执行得更好。这完全取决于您。

树的平面表示的问题是您必须定期重建或不断维护平面结构。根据您的查询路径将确定您应该使用哪种展平算法,但最终结果保持不变。平面结构是“完成”您想在 EF 中执行的操作而无需通过 DBConnection 欺骗和执行原始 SQL 的唯一方法。

关于c# - EF Code First 提高自引用性能,一对多关系,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11619315/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com