You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

EF Core 7中FromSqlInterpolated重复Id键错误的解决咨询

解决EF Core自定义SQL查询的重复Id列冲突问题

问题根源

你遇到的错误本质是:自定义SQL返回了多个同名的Id列(比如User.Id、StoryContainer.Id,还有可能关联的UserFriend.Id),EF Core在解析结果集时,会将这些同名列视为同一个键,导致抛出"An item with the same key has already been added"异常。

而EF自动生成的查询之所以正常,是因为EF会自动给关联实体的列添加区分别名(比如将User.Id映射为Author_Id,StoryContainer.Id保留为Id),以此避免列名冲突,同时让EF能正确识别哪些列属于主实体,哪些属于导航属性。

具体解决方案

方案1:给SQL中的重复Id列添加EF可识别的别名

如果你的目标是查询StoryContainerRto并关联其Author(好友用户),需要修改SQL,给用户表的Id及其他属性添加以Author_为前缀的别名,让EF能自动映射到StoryContainerRto.Author导航属性:

假设原SQL是:

SELECT u.Id, u.Nickname, sc.Id, sc.AuthorId
FROM UserFriend uf
JOIN User u ON uf.SecondFriendId = u.Id
JOIN StoryContainer sc ON u.Id = sc.AuthorId
WHERE uf.FirstFriendId = {currentUserId}

修改后的SQL:

SELECT 
  sc.Id, sc.AuthorId,
  u.Id AS Author_Id, u.Nickname AS Author_Nickname,
  -- 按需添加其他User属性,都加上Author_前缀
FROM UserFriend uf
JOIN User u ON uf.SecondFriendId = u.Id
JOIN StoryContainer sc ON u.Id = sc.AuthorId
WHERE uf.FirstFriendId = {currentUserId}

然后执行查询:

var friendStories = await dbContext.StoryContainers
    .FromSqlInterpolated($"上面修改后的SQL")
    .Include(sc => sc.Author) // 这里Include会生效,因为别名匹配导航属性的列映射规则
    .ToListAsync();

方案2:使用DTO避免实体映射冲突

如果不需要返回完整的EF实体,只需要查询所需字段,建议创建一个DTO类,彻底绕开实体属性的列名限制:

  1. 创建DTO:
public class FriendStoryDto
{
    public int FriendId { get; set; }
    public string FriendNickname { get; set; }
    public int StoryContainerId { get; set; }
    // 按需添加其他需要的字段
}
  1. 修改SQL返回与DTO属性匹配的列名:
SELECT 
  u.Id AS FriendId, u.Nickname AS FriendNickname,
  sc.Id AS StoryContainerId
FROM UserFriend uf
JOIN User u ON uf.SecondFriendId = u.Id
JOIN StoryContainer sc ON u.Id = sc.AuthorId
WHERE uf.FirstFriendId = {currentUserId}
  1. 执行查询:
var friendStories = await dbContext.Set<FriendStoryDto>()
    .FromSqlInterpolated($"上面的SQL")
    .ToListAsync();

方案3:通过[Column]属性强制指定列映射

如果不想修改SQL,可以在实体属性上添加[Column]特性,指定该属性对应的SQL列名。比如在UserRto的Id属性上添加:

[Key]
[Column("UserId")]
public int Id { get; set; }

但这种方法会影响该实体的所有查询,可能引发其他问题,仅适合特殊场景。

关键注意点

  • EF Core使用列名来匹配实体属性,所以结果集中的列名必须唯一,且与实体(或DTO)的属性名(或[Column]指定的名称)完全匹配。
  • 对于导航属性,EF默认会识别导航属性名_属性名格式的别名,比如Author_Id对应StoryContainerRto.Author.Id。

内容的提问来源于stack exchange,提问作者Семён Титов

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 03:27:39