public class CarDTO
{
public int CarId { get; set; }
public string CarName { get; set; }
public List<PolicySummaryDTO> PolicySummaries { get; set; }
}
public class PolicySummaryDTO
{
public long PolicyId { get; set; }
public string PolicyName { get; set; }
public decimal Amount { get; set; }
}
我有List<CarDTO> Cars
的每辆车已经有政策列表List<PolicySummaryDTO> PolicySummaries
。
PolicyId
已填写。但是其他属性,例如PolicyName
,Amount
等则不是。
我需要做的是从数据库_contex.PolicySummary
获取数据,我需要填写缺少的字段。
我知道我可以这样操作。
获取所有PolicyId
var policyIds = cars.SelectMany(t => t.PolicySummaries).Select(r => r.PolicyId);
基于ID获取PolicyData
var policyData = _context.PolicySummary.Where(t => policyIds.Contains(t.PolicyId));
然后使用 foreach 我可以填充数据。
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;
}
}
一切都可以正常工作,但是我想知道是否可以以更优雅的方式做到这一点,也许可以使用LINQ JOIN
之类的东西,或者我的解决方案是完全正确的?
编辑-词典解决方案
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;
}
}
答案 0 :(得分:1)
那么,首先,为什么没有填充数据?在使用Car
从数据库实际进行.Include
提取期间,您应该能够在任何相关实体中填写数据。
var carEntity = _context.Cars.Where(predicate).Include(c => c.PolicySummaries).Single();
第二,您的代码遇到严重的性能问题:policyData
是IQueryable
。每次执行policyData.Single
时,都会向数据库发送一个新查询,浏览所有策略,过滤在id
中具有policyIds
的策略,然后选择适当的策略。如果有很多策略,这将是非常低效的-每个foreach
迭代都是一个新查询!您应该做的大概是:
var policyData = _context.PolicySummary.Where(t => policyIds.Contains(t.PolicyId)).ToList();
获取所有相关策略。如果有很多共享策略,而您最终还是在某个时候加载了大多数共享策略,那么这也可能是效率低下的,因此,如果数据库中没有十亿个策略,那么:
var policies = _context.PolicySummary.ToList();
可以更好地工作。此时,这是时间/内存的折衷,但是请注意,上述任何一项都会比当前的查询过载foreach
更好。
答案 1 :(得分:0)
由于您已经存在DTO对象(而非实体),因此无法将Join
与EntityFramework一起有效使用。
您需要加载丢失的数据,然后使用它来更新现有的汽车对象。
我认为更简单的方法是将策略列表替换为从数据库中检索到的对象。
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();
}
使用Dictionary
将使更新代码更加简单,并减少执行操作的次数,减少循环。
注意:上面的方法假定数据库中存在所有策略ID。否则,将从相应的car.Policies
属性中删除它们。您可以删除Where(allPolicies.ContainsKey)
行,如果缺少某些策略,则会引发异常。