【问题标题】:LINQ join failing between table and list of objects表和对象列表之间的 LINQ 连接失败
【发布时间】:2020-10-17 20:20:54
【问题描述】:

我需要使用 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 中的错误或限制。有关更多详细信息,请参阅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();

【问题讨论】:

  • 什么是 kuku 和 riku?
  • 你不能用_context.Room加入一个本地列表,它不能被翻译成SQL。
  • 在获得所有数据之前,您无法处理数据。那么你是如何接收数据的呢?您使用什么来确定何时收到所有数据?
  • @TylerH 这个老问题需要提出来吗?
  • @CaiusJard 标签需要修复。而且,它不是那么旧。

标签: c# linq .net-core


【解决方案1】:

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

【讨论】:

    【解决方案2】:

    LINQ 并不是要更改源,它只能从源中提取数据。如果需要更新数据,首先获取必须更新的项目,然后更新它们。或者,您可以使用普通的旧 SQL 来更新数据,而无需先获取它。

    在本地内存中,您有一个RoomsForUpdate 序列。每个 RoomForUpdate 都有一个 Id (RuId) 和一个名称。

    在您的数据库中,您有一个带有Rooms 的表,该表中的每个RoomRoomId 中都有一个ID 和一个RoomAddress

    在我看来,您想要更新所有具有RoomIdRooms,这是您的RoomsForUpdate 序列中的RuIds 之一。换句话说:获取具有 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();
    

    【讨论】:

      猜你喜欢
      • 2013-04-05
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-09-15
      • 1970-01-01
      • 2014-08-21
      • 2012-10-28
      相关资源
      最近更新 更多