EF Core开启UseLazyLoadingProxies后record类型实体报错如何解决
问题成因与解决方案
1. 报错成因及替代解决方案
成因
EF Core 的懒加载代理基于 Castle.Core 动态代理库实现,运行时会生成实体类的派生代理类,重写所有 virtual 属性以注入懒加载逻辑。
C# 中 record 类型会由编译器隐式生成一个名为 <Clone>$() 的 protected virtual 方法(即报错信息中提到的 $() 方法),用于支持 with 表达式的非破坏性复制,该方法的返回值为当前 record 的具体类型,而非通用父类类型。
.NET 6 对应的 EF Core 版本和 Castle.Core 版本未对 record 的这个特殊生成方法做兼容处理,生成代理类时尝试按普通虚方法规则重写 <Clone>$(),因返回类型协变校验不通过触发类型不兼容报错。
非弃用record的解决方案
- 显式密封
<Clone>$()方法:在所有record实体中手动声明并密封该编译器生成方法,阻止代理类尝试重写,示例如下:public record ListViewConfig { // 其他属性定义 protected sealed override ListViewConfig <Clone>$() => this; } - 改用显式懒加载实现:不使用代理式懒加载,在实体中注入
ILazyLoader手动实现导航属性的懒加载逻辑,不需要生成动态代理即可实现懒加载,避免代理与record的冲突。 - 升级框架版本:EF Core 7.0 及以上版本已原生兼容
record类型与懒加载代理的适配,将 SDK 与 EF Core 依赖升级到 7.0+ 版本即可直接解决该问题。
2. record Clone方法的适配问题
record 的 <Clone>$() 是编译器生成的特殊虚方法,和普通自定义虚方法有以下差异,导致早期动态代理逻辑无法适配:
- 返回类型为当前
record的闭合具体类型,而非父类类型,普通代理重写逻辑未对该方法做返回类型协变的特殊处理; - 方法名包含特殊字符
$,属于编译器保留的特殊命名方法,旧版本 Castle 代理生成逻辑未识别该方法的特殊性,会默认尝试重写所有虚方法,触发类型校验失败。
内容的提问来源于stack exchange,提问作者CleanCoder
相关产品推荐
相关产品推荐

