簡體   English   中英

LINQ 在表和對象列表之間連接失敗

[英]LINQ join failing between table and list of objects

我需要使用 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 Core 中的錯誤或限制。 有關更多詳細信息,請參閱鏈接

    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();

您不能將 EF Core LINQ 查詢與本地列表相結合,因為它無法轉換為 SQL。 最好先獲取數據庫數據,然后加入 memory。

LINQ 並不是要更改源,它只能從源中提取數據。 如果需要更新數據,首先獲取必須更新的項目,然后更新它們。 或者,您可以使用普通的舊 SQL 來更新數據,而無需先獲取數據。

在本地 memory 中,您有一個RoomsForUpdate序列。 每個 RoomForUpdate 都有一個 Id (RuId) 和一個名稱。

在您的數據庫中,您有一個帶有Rooms的表,該表中的每個RoomRoomId中都有一個 Id 和一個RoomAddress

在我看來,您想要更新所有具有RoomIdRooms ,這是您的RuIds序列中的 RuId 之一。 換句話說:獲取具有 RoomId 值的所有房間(的某些屬性),該值是您的 RoomsForUpdate 序列中的 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 語句中。 這不會提高效率,但是會降低代碼的可讀性。

現在要更新房間,您必須一一枚舉它們,並為獲取的房間賦予新值。 你沒有說你想要哪個新值。 我有一個暗示,您想將 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();

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM