如何在C#中基于ID在嵌套列表中填充数据?
一切都会很好,但我想知道我是否可以用更优雅的方式来做,可能是使用如何在C#中基于ID在嵌套列表中填充数据?,c#,entity-framework,linq,entity-framework-core,C#,Entity Framework,Linq,Entity Framework Core,一切都会很好,但我想知道我是否可以用更优雅的方式来做,可能是使用linqjoin或其他什么,或者我的解决方案完全正确 使用字典编辑解决方案 foreach (var car in cars) { foreach (var policy in car.PolicySummaries) { var policyDataToUse = policyData.Single(t => t.PolicyId == policy.PolicyId); p
linqjoin
或其他什么,或者我的解决方案完全正确
使用字典编辑解决方案
foreach (var car in cars)
{
foreach (var policy in car.PolicySummaries)
{
var policyDataToUse = policyData.Single(t => t.PolicyId == policy.PolicyId);
policy.Amount = policyDataToUse.Amount;
policy.PolicyName = policyDataToUse.PolicyName;
}
}
那么首先,为什么数据没有填写?在使用
从数据库实际提取车辆期间,您应该能够填写任何相关实体中的数据。包括
var policyIds = cars.SelectMany(t => t.PolicySummaries).Select(r => r.PolicyId);
var policyDict = _context.PolicySummary.Where(t => policyIds.Contains(t.PolicyId)).ToDictionary(t => t.PolicyId);
foreach (var car in cars)
{
foreach (var policy in car.PolicySummaries)
{
var policyDataToUse = policyDict[policy.PolicyId];
policy.Amount = policyDataToUse.Amount;
policy.PolicyName = policyDataToUse.PolicyName;
}
}
其次,您的代码存在严重的性能问题:policyData
是一个IQueryable
。每次执行policyData.Single
,都会向数据库发送一个新的查询,查看所有策略,筛选在policyIds
中具有id
的策略,并选择适当的一个。如果有很多策略,这将是非常低效的-每个foreach
迭代都是一个新的查询!您应该做的可能是:
var carEntity = _context.Cars.Where(predicate).Include(c => c.PolicySummaries).Single();
获取所有相关策略。如果有很多共享策略,并且您最终在某个时候加载了其中的大部分,那么这也可能是低效的,因此,如果数据库中没有10亿个策略,那么:
var policyData = _context.PolicySummary.Where(t => policyIds.Contains(t.PolicyId)).ToList();
可以做得更好。在这一点上,这是一个时间/内存权衡,但请注意,上述任何一项都比当前的查询重载<;code>foreach
要好。因为您已经存在到对象(而不是实体)的数据,您将无法有效地使用EntityFramework的连接。
您需要加载丢失的数据,然后用它更新现有的汽车对象
我认为更简单的方法是用从数据库检索的对象替换策略列表
var policies = _context.PolicySummary.ToList();
使用字典
将使更新代码更简单,并减少要执行的操作量,减少循环
注意:上述方法假设数据库中存在所有策略Id。如果没有,这些将从相应的car.Policies
属性中删除。您可以删除Where(allPolicies.ContainsKey)
行,如果缺少某些策略,将引发异常。您使用的是EF吗?或者如何将对象映射到实体?如果您正在使用任何框架或ORM,那么应该有一种“正确”的方法来获取您的属性。请编辑您的问题,并向我们提供有关项目如何设置的详细信息。@nilsK实际上在这个问题中有一个标记“实体框架”。@Fabjan是的,我在他的评论后添加:)这是因为PolicySummaryDTO
中还有一个列表。并且该policysumarydto
中的PolicyId
是根据该列表中的第一条记录填写的。这是个复杂的案子,我知道。我也不想让这个问题复杂化。您仍然应该能够在db fetch中实现这一点,但如果您希望单独实现,那么至少要坚持我的第二点。从效率的角度来说应该没问题。使用Single
或SingleOrDefault
取决于您想要实现什么。如果您在代码中确定数据库中应该有一条具有给定Id
的记录,则使用Single
。对于不存在的实体,我通常更喜欢我的存储库抛出异常,而不是返回null
s,但这取决于具体情况。总之,说“不要使用”不是一个好建议。两者都有适当的用例。总是尽早崩溃。更好的方法是抛出一个异常并处理它——记录一个错误,告诉用户出了什么问题,然后继续。如果这不应该发生,那就是个例外。如果默认值是合理的、可用的值,则返回它。“Smth不正确”总是推断出一种应尽快提出的异常情况。@Lewis86-当错误是业务逻辑的异常时抛出异常。当数据库中必须只存在一个具有特定id的记录时,Single
将是更容易理解的方法。通常,您在应用程序的顶层(UI或Web API)捕获所有错误,记录它们,并为用户显示/返回友好的错误消息。记录的异常提供有关错误原因的足够信息。作为奖励,应用程序的其余部分不需要处理“默认”值,这将简化代码库。当可以将它们展平并更新每个元素中的几个字段时,将新集合分配给car.Policies
,就没有意义了
call将返回一个新的集合,该集合可能是理想的行为,也可能不是理想的行为here@Fabjan,正如您所说,这可能是一种可取的行为,也可能不是一种可取的行为;)。作为一个好处,如果要向PolicySummaryDTO类添加/删除新属性,则无需更改更新代码。Part with dictionary返回我System.ArgumentException:'已添加具有相同键的项。关键字:0'
@DiPix,在创建字典之前,您可以根据PolicyId
对项目进行分组,检查更新的答案。Downvoter,请留下评论,将很乐意修复或删除我的答案。
var policies = _context.PolicySummary.ToList();
var allPolicies = _context.PolicySummary
.Where(policy => policyIds.Contains(policy.PolicyId))
.Select(group => new PolicySummaryDTO
{
PolicyId = group.Key,
PolicyName = group.First().PolicyName,
Amount = group.First().Amount
})
.ToDictionary(policy => policy.PolicyId);
foreach (var car in cars)
{
car.Policies = car.Policies.Select(p => p.PolicyId)
.Where(allPolicies.ContainsKey)
.Select(policyId => allPolicies[policyId])
.ToList();
}