表和对象列表之间的LINQ联接失败

7hiiyaii  于 2023-01-15  发布在  其他
关注(0)|答案(2)|浏览(123)

我需要在C# .NET Core 3.0中更新一个表,该表包含对象列表中的值。我尝试使用Join方法,但收到以下错误:
LINQ表达式的处理

DbSet<Room>
    .Join(
        outer: __p_0, 
        inner: p => p.RoomId, 
        outerKeySelector: s => s.ruId, 
        innerKeySelector: (s, p) => new { 
            kuku = s, 
            riku = p
         })

“NavigationExpandingExpressionVisitor”执行的操作失败。这可能表示EF核心中存在错误或限制。有关详细信息,请参阅link

public class Room
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        [Key]
        public int RoomId { get; set; }

        [StringLength(50, MinimumLength = 3)]
        public string RoomAddress { get; set; }
    }
    public class roomsForUpdate 
    {
        public int ruId { get; set; }
        public string ruName { get; set; }
    }

var roomList = new List<roomsForUpdate>() { new roomsForUpdate  { ruId = 1, ruName = "aa" }, new roomsForUpdate { ruId = 2, ruName = "bb" } };
var result = _context.Room.Join(roomList, p => p.RoomId, s => s.ruId, (s, p) => new { kuku = s, riku = p }).ToList();
q8l4jmvw

q8l4jmvw1#

无法将EF Core LINQ查询与本地列表联接,因为它无法转换为SQL。最好先获取数据库数据,然后在内存中联接。

uqcuzwp8

uqcuzwp82#

LINQ并不打算更改源,它只能从源中提取数据。如果需要更新数据,请先获取必须更新的项,然后再更新它们。或者,您可以使用普通的旧SQL来更新数据,而不必先获取数据。
在本地内存中,有一个序列RoomsForUpdate,每个RoomForUpdate都有一个Id(RuId)和一个Name。
在您的数据库中,您有一个Rooms表,该表中的每个Room都有一个RoomId ID和一个RoomAddress ID。
在我看来,您似乎希望更新所有具有RoomIdRooms,即RoomsForUpdate序列中的RuIds之一。换句话说:获取RoomsForUpdate序列中RoomId值为RuId的所有房间(的某些属性):

var roomsToUpdate = new List<roomsForUpdate>()
{
    new roomsForUpdate  { ruId = 1, ruName = "aa" },
    new roomsForUpdate { ruId = 2, ruName = "bb" }
};

// Extract the Ids of the rooms that must be fetched
var roomToUpdateIds = roomsToUpdate.Select(room => room.ruId);

// Fetch all rooms from the database that have a RoomId that is in this sequence
var fetchedRooms = dbContext.Rooms
    .Where(room => roomToUpdateIds.Contains(room => room.RoomId)
    .ToList();

当然,你可以把所有的东西都放到一个大的LINQ语句中。这不会提高效率,但是会降低代码的可读性。
现在要更新Rooms,您必须逐个枚举它们,并为获取的Rooms赋予新值。您没有说您想要哪个新值。我有一种暗示,您想要将RuName赋给RoomAddress。这意味着您必须将Room与RoomAddress的新值组合在一起。
这可以通过LINQ完成:

var roomsWithExpectedNewValues = fetchedRooms.Join(roomsToUpdate,

    fetchedRoom => fetchedRoom.RoomId,   // from every fetched room take the Id
    roomToUpdate => roomToUpdate.RuId,   // from every room to update take the RuId

    // for every fetchedRoom with its matching room to update, make one new:
    (fetchedRoom, roomToUpdate) => new
    {
        Room = fetchedRoom,
        NewValue = roomToUpdate.RuName,
    })
    .ToList();

要实际执行更新,您必须枚举以下序列:

foreach (var itemToUpdate in roomsWithExpectedNewValues)
{
    // assign RuName to RoomName
    itemToUpdate.Room.RoomName = itemToUpdate.NewValue;
}
dbContext.SaveChanges();

少一点LINQ

虽然这是可行的,但似乎有很多魔法在进行。连接将在内部创建一个用于快速查找的字典,并将其丢弃。我认为少一点LINQ将使理解发生了什么更容易。

// your original roomsToUpdate
var roomsToUpdate = new List<roomsForUpdate>()
{
    new roomsForUpdate  { ruId = 1, ruName = "aa" },
    new roomsForUpdate { ruId = 2, ruName = "bb" }
};

var updateDictionary = roomsToUpdate.ToDictionary(
    room => room.RuId,         // key
    room => room.RuName)       // value

字典的关键字是您要获取的房间的ID:

// fetch the rooms that must be updated:
var fetchedRooms = dbContext.Rooms
    .Where(room => updateDictionary.Keys.Contains(room => room.RoomId)
    .ToList();

// Update:
foreach (var fetchedRoom in fetchedRooms)
{
    // from the dictionary fetch the ruName:
    var ruName = updateDicationary[fetchedRoom.RoomId];

    // assign the ruName to RoomAddress
    fetchedRoom.RoomAddress = ruName;

    // or if you want, do this in one statement:
    fetchedRoom.RoomAddress = updateDicationary[fetchedRoom.RoomId];
}
dbContext.SaveChanges();

相关问题