Linq to Entities 并更新记录

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

我有以下结构:

public interface IChainByPreviousId
{
    int Id { get; set; }
    int? PreviousDeviceId { get; set; }
}

public class RegisteredDevice : IChainByPreviousId
{
    public int Id { get; set; }

    public int? PreviousDeviceId { get; set; }
    public int? Position { get; set; }

    public string DeviceName { get; set; }
    public string ModelNumber { get; set; }

    public virtual RegisteredDevice? PreviousDevice { get; set; }
}

因此,正如我们所看到的,每个设备都可以有以前的设备或为空(如果它是我们订购列表中的第一个设备)

        builder.HasOne(a => a.PreviousDevice)
            .WithOne()
            .HasForeignKey<RegisteredDevice>(p => p.PreviousDeviceId)
            .OnDelete(DeleteBehavior.NoAction);

然后我想“重新排序”设备。

model.DeviceReordered

的集合
public class DeviceReorderedDto
{
    public int DeviceId { get; set; }
    public int? NewPreviousDeviceId { get; set; }
}

所以,这个类描述了重新排序设备(新的先前的deviceId)

对于我的情况,有(将 Id == 2 的设备从第二个位置(id=1 之后)移动到 id == 4 和 id == 5 之间的设备):

[
  {
    "DeviceId": 3,
    "NewPreviousDeviceId": 1
  },
  {
    "DeviceId": 2,
    "NewPreviousDeviceId": 4
  },
  {
    "DeviceId": 5,
    "NewPreviousDeviceId": 2
  }
]

获取设备:

var localDevices = _dataContext.RegisteredDevices.ToList();

init

localDevices
(获取数据之后和应用更改之前)如下:

[
  {
    "Id": 1,
    "ModelNumber": "20I",
    "CompanyName": "J. J. Keller \u0026 Associates, Inc.",
    "DeviceName": "J. J. Keller ELD - iOS 2.0",
    "PreviousDeviceId": null,
    "Position": 0
  },
  {
    "Id": 2,
    "ModelNumber": "25I",
    "CompanyName": "J. J. Keller \u0026 Associates, Inc.",
    "DeviceName": "J. J. Keller ELD - iOS 2.5",
    "PreviousDeviceId": 1,
    "Position": 1
  },
  {
    "Id": 3,
    "ModelNumber": "FLT3",
    "CompanyName": "HOS247 LLC",
    "DeviceName": "#1 ELD by HOS247",
    "PreviousDeviceId": 2,
    "Position": 2
  },
  {
    "Id": 4,
    "ModelNumber": "N775G",
    "CompanyName": "XPERT-IT SOLUTIONS INC.",
    "DeviceName": "Xpert ELD",
    "PreviousDeviceId": 3,
    "Position": 3
  },
  {
    "Id": 5,
    "ModelNumber": "PMG001",
    "CompanyName": "PeopleNet",
    "DeviceName": "PeopleNet Mobile Gateway - Trimble Driver ELD",
    "PreviousDeviceId": 4,
    "Position": 4
  }
]

并应用更改:

for (int i = 0; i < model.DeviceReordered.Count; i++)
{
   var item = model.DeviceReordered[i];
   var device = localDevices.Where(a => a.Id == item.DeviceId).First();
   var previousDevice = localDevices.Where(a => a.Id == item.NewPreviousDeviceId).FirstOrDefault();
   device.PreviousDevice = previousDevice;
}
localDevices = localDevices.OrderBy(a => a.Position).ToList();

并保存更改:

        await _dataContext.SaveChangesAsync();

我遇到错误:

无法在对象“dbo.RegisteredDevice”中插入重复的键行 唯一索引“IX_RegisteredDevice_PreviousDeviceId”。重复的键 值为 (4)。

但是我在

localDevices
中没有重复项 好的,在致电
await _dataContext.SaveChangesAsync();
之前我添加:
ValidateDuplicates(list);

    void ValidateDuplicates(IList<T> positionList)
    {
        var duplicates = positionList
            .Where(x => x.PreviousDeviceId.HasValue)
            .GroupBy(x => x.PreviousDeviceId)
                                        .Where(g => g.Count() > 1)
                                        .Select(x => x.Key).ToList();
        if (duplicates.Any())
            throw new PositionReorderDuplicateException(duplicates);
    }

无重复。好的,再检查一下:

var prev = localDevices.Where(a => a.PreviousDeviceId == 4).ToList();

prev
只有一条记录。

我的

localDevices
打电话前
await _dataContext.SaveChangesAsync()

[
  {
    "Id": 1,
    "ModelNumber": "20I",
    "CompanyName": "J. J. Keller \u0026 Associates, Inc.",
    "DeviceName": "J. J. Keller ELD - iOS 2.0",
    "PreviousDeviceId": null,
    "Position": 0
  },
  {
    "Id": 3,
    "ModelNumber": "FLT3",
    "CompanyName": "HOS247 LLC",
    "DeviceName": "#1 ELD by HOS247",
    "PreviousDeviceId": 1,
    "Position": 1
  },
  {
    "Id": 4,
    "ModelNumber": "N775G",
    "CompanyName": "XPERT-IT SOLUTIONS INC.",
    "DeviceName": "Xpert ELD",
    "PreviousDeviceId": 3,
    "Position": 2
  },
  {
    "Id": 2,
    "ModelNumber": "25I",
    "CompanyName": "J. J. Keller \u0026 Associates, Inc.",
    "DeviceName": "J. J. Keller ELD - iOS 2.5",
    "PreviousDeviceId": 4,
    "Position": 3
  },
  {
    "Id": 5,
    "ModelNumber": "PMG001",
    "CompanyName": "PeopleNet",
    "DeviceName": "PeopleNet Mobile Gateway - Trimble Driver ELD",
    "PreviousDeviceId": 2,
    "Position": 4
  }
]

所以,看来一切都好。按预期重新排序,没有重复,只有一台设备有

PreviousDeviceId == 4

我的完整代码是:

        var localDevices = _dataContext.RegisteredDevices.ToList();

        if (model.DeviceReordered.Any())
        {
            for (int i = 0; i < model.DeviceReordered.Count; i++)
            {
                var item = model.DeviceReordered[i];
                var device = localDevices.Where(a => a.Id == item.DeviceId).First();
                var previousDevice = localDevices.Where(a => a.Id == item.NewPreviousDeviceId).FirstOrDefault();
                device.PreviousDeviceId = previousDevice?.Id;
            }
        }

        int? previousId = null;
        var liveRecordsCount = localDevices.Where(a => a.Position.HasValue).Count();

        for (int i = 0; i < liveRecordsCount; i++)
        {
            var device = localDevices.Where(a => a.PreviousDeviceId == previousId).First();
            device.Position = i;
            previousId = device.Id;
        }

        localDevices = localDevices.OrderBy(a => a.Position).ToList();

        await _dataContext.SaveChangesAsync();

已编译的项目,其中包含我添加到的问题https://github.com/oshastitko/reordering_problem

我的代码有什么问题?

c# linq-to-entities savechanges
1个回答
0
投票

我建议将修改分成几个步骤(最好是在一个事务中,这样就不会发生不一致的状态) - 1)“清理”所有更改项目的先前设备 ID 2)更新先前的设备 ID 3)重新计算位置:

// clean the previous ids
_dataContext.RegisteredDevices
    .Where(device => model.Select(dto => dto.DeviceId).Contains(device.Id))
    .ExecuteUpdate(up => up.SetProperty(device => device.PreviousDeviceId, (int?)null));

// set new ones
var changedDevices = _dataContext.RegisteredDevices
    .Where(device => model.Select(dto => dto.DeviceId).Contains(device.Id))
    .ToList();
for (int i = 0; i < model.Count; i++)
{
    var item = model[i];
    var device = changedDevices.Where(a => a.Id == item.DeviceId).First();
    device.PreviousDeviceId = item.NewPreviousDeviceId;
}
_dataContext.SaveChanges();

// recalculate and update the positions
_dataContext.ChangeTracker.Clear();
var localDevices = _dataContext.RegisteredDevices.ToList();
// ...

启用 查询日志记录,据我记得 EF 会按 1 发送更新,这样当您尝试插入仍被某人使用的

PreviousDeviceId
时,您可能会遇到这样的情况。

提交了 PR,其中包含对我有用的代码。

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