我有AccountGroup
这是一个自引用实体。叶子AccountGroup
可以包含1个或多个Accounts
。两个实体都具有Balance
属性。每个AccountGroup
都有Balance
,它可以是子组中Balance
的总和,也可以是所有帐户Balance
的总和(如果是叶组)。< / p>
为了构建所有AccountGroup
和Account
的树列表,我必须递归地遍历这个对象图,这会导致很多(我的意思是很多!!!)调用DB ...
有没有办法以这样的方式改进这种方式,以减少数据库调用数量?
由于
这是修剪后的代码
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; }
}
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);
}
}
}
答案 0 :(得分:0)
CTE方法
有两种方法可以提高针对树数据类型的查询速度。第一个(也可能是最简单的)是使用存储过程和EF的执行sql功能来加载树。 SProc将缓存,结果集执行速度将提高。我对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查询构建平面结构。
这只是一种方法,但有很多关于这个问题的论文:
编辑:添加其他说明 只需注意,递归CTE缓存是在迭代结构之前查询的符号。这是编写查询以解决问题的最快速,最简单的方法。但是,这个HAS是一个SQL查询。您可以直接使用execute sql,也可以执行SProc。 Sprocs在运行后缓存执行图,因此它们比在运行之前必须构建执行计划的本机查询执行得更好。这完全取决于你。
树的平面表示问题是您必须定期重建或不断维护平面结构。根据您的查询路径,将确定您应该使用哪种展平算法,但最终结果保持不变。扁平结构是在EF中“完成”您想要做的事情的唯一方法,而不必通过DBConnection欺骗和执行原始SQL。