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类,彻底绕开实体属性的列名限制:
- 创建DTO:
public class FriendStoryDto { public int FriendId { get; set; } public string FriendNickname { get; set; } public int StoryContainerId { get; set; } // 按需添加其他需要的字段 }
- 修改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}
- 执行查询:
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,提问作者Семён Титов
相关产品推荐
相关产品推荐

