如何在C#中基于ID的嵌套列表中填充数据?

时间:2018-08-14 08:02:12

标签: c# entity-framework linq entity-framework-core

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> PolicySummariesPolicyId已填写。但是其他属性,例如PolicyNameAmount等则不是。 我需要做的是从数据库_contex.PolicySummary获取数据,我需要填写缺少的字段。

我知道我可以这样操作。

  1. 获取所有PolicyId var policyIds = cars.SelectMany(t => t.PolicySummaries).Select(r => r.PolicyId);

  2. 基于ID获取PolicyData var policyData = _context.PolicySummary.Where(t => policyIds.Contains(t.PolicyId));

  3. 然后使用 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;
    }
}

2 个答案:

答案 0 :(得分:1)

那么,首先,为什么没有填充数据?在使用Car从数据库实际进行.Include提取期间,您应该能够在任何相关实体中填写数据。

var carEntity = _context.Cars.Where(predicate).Include(c => c.PolicySummaries).Single();

第二,您的代码遇到严重的性能问题:policyDataIQueryable。每次执行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)行,如果缺少某些策略,则会引发异常。