集合已修改;枚举操作可能无法在实体框架拦截器上执行

问题描述 投票:0回答:1

嗨,我正在尝试通过 postgres 上的实体框架 8.0 一次存储大约 7000 个对象

var citiesJson = File.ReadAllText("Schemas/delivery-cities.json");
var cities = JsonSerializer.Deserialize<List<DeliveryCityDTO>>(citiesJson);
var tasks = cities.Select(x => _cityService.Seed(x)).ToList();
await Task.WhenAll(tasks);

这就是我准备任务的方式以及任务中发生的情况:

public async Task Seed(DeliveryCityDTO city)
{
    var deliveryService = await _context.DeliveryServices.SingleAsync(x => x.Name == city.DeliveryServiceName);

    DeliveryCity deliveryCity = new()
    {
        CityFiasСode = city.CityFiasСode,
        Name = city.Name,
    };

    _context.DeliveryCities.Add(deliveryCity);

    _context.DeliveryServiceDeliveryCities.Add(new DeliveryServiceDeliveryCity
    {
        RefDeliveryCity = deliveryCity.Id,
        RefDeliveryService = deliveryService.Id
    });

    await _context.SaveChangesAsync();
}

我还配置了 AuditableEntityInterceptor 来运行可审核的基本实体,这两个实体都不是可审核的实体,并且时不时地我得到“集合已修改;”枚举操作可能无法执行',这位于以下行:

if (context.ChangeTracker.Entries<BaseAuditableEntity>().Count() == 0) return;

请帮助避免这个问题,我看到 postgres 表已满,但这个错误很烦人。

c# entity-framework-core .net-8.0
1个回答
0
投票

从您的代码中不清楚为什么抛出异常。没有什么明显改变列表

cities
tasks
。它可能是在多个线程中使用一个上下文实例的产物,因为这往往会导致各种不可预测的异常。无论如何,这是您应该更改的第一件事,因为上下文不是线程安全的。

但还有更多。该代码也非常低效(或chatty)。您单独处理每个城市,每个城市两次往返。即使并行执行,这也可能不是最快的方法。

最好的方法是一次性处理所有城市。 EF core 使插入(和更新)比以前更加块状(即在一个查询批次中执行更多操作),但是,当然,只有当有需要在块中执行操作时。

考虑到所有这些,根据您显示的代码,我们将努力改进您的代码:

public async Task Seed(DeliveryCityDTO[] cities)
{
    var names = cities.Select(c => c.DeliveryServiceName).ToList();

    var deliveryServices = await _context.DeliveryServices
        .Where(ds => names.Contains(ds.Name))
        .Select(ds => new { ds.Name, ds.Id })
        .ToDictionary(ds => ds.Name, ds => ds.Id);

    var deliveryCities = cities
        .Select(x => new DeliveryCity
        {
            CityFiasСode = city.CityFiasСode,
            Name = city.Name,
            DeliveryServiceDeliveryCity = new DeliveryServiceDeliveryCity
            {
                RefDeliveryService = deliveryServices[x.DeliveryServiceName]
            }
        });

    _context.DeliveryCities.AddRange(deliveryCities);

     await _context.SaveChangesAsync();
}

这假设

City
具有导航属性
DeliveryServiceDeliveryCity
。如果没有,你最好添加它。

我非常有信心,这个单一的

SaveChanges
调用很容易胜过并行化过程。

© www.soinside.com 2019 - 2024. All rights reserved.