EF7 DB-First:Scaffold-DBContext生成的模型无ICollection构造函数与setter
EF7 Scaffold-DBContext 模型类变更问题解答
问题背景
将EntityFramework相关NuGet包(.Design、.SqlServer、.Tools)升级至7.0后,使用Scaffold-DBContext从数据库反向工程生成模型类时,发现模型结构与EF6版本存在差异,导致大量项目编译错误。
EF6生成的模型示例:
public partial class Foo { public Foo() { Deps = new HashSet<Dep>(); } public int FooId { get; set; } public int BossId { get; set; } public virtual Boss Boss { get; set; } = null!; public virtual ICollection<Dep> Deps { get; set; } }
EF7生成的模型示例:
public partial class Foo { public int FooId { get; set; } public int BossId { get; set; } public virtual Boss Boss { get; set; } = null!; public virtual ICollection<Dep> Deps { get; } = new List<Dep>(); }
核心差异:
- EF7生成的模型没有初始化
HashSet的构造函数 - 集合属性
ICollection<T>仅保留get访问器,移除了set访问器
用户核心疑问:
- 为何进行此变更?
- 如何解决该限制带来的问题?
- 是否有选项让EF7沿用EF6的生成方式?
问题解答
1. 变更原因
这个调整是EF团队为强化实体类的不可变性设计而做出的:
- 只读集合属性能避免外部代码随意替换整个集合实例,减少因集合被意外替换导致的EF上下文跟踪异常
- 直接在属性初始化时创建
List<T>,替代构造函数初始化的HashSet<T>,简化了实体实例化逻辑,同时EF内部对List<T>的跟踪效率更稳定 - 符合现代.NET开发中推崇的不可变对象设计原则,提升实体类的可靠性和可维护性
2. 解决现有代码适配问题
针对从外部构建带依赖集合的新实体场景,有几种可行方案:
- 直接向集合添加元素:集合是只读但可修改的(
ICollection<T>本身支持Add/AddRange等方法),无需替换整个集合实例,直接调用Deps.Add()或Deps.AddRange(外部数据集合)即可var foo = new Foo(); foo.Deps.AddRange(externalDepList); dbContext.Foos.Add(foo); - 添加自定义构造函数:利用EF支持的构造函数绑定特性,在生成的
partial类中补充带集合参数的构造函数(不要修改自动生成的文件,避免重新生成时被覆盖)public partial class Foo { public Foo(IEnumerable<Dep> deps) { Deps.AddRange(deps); } } // 使用方式: var foo = new Foo(externalDepList); - 使用集合初始化语法:EF7支持集合初始化语法直接向只读集合添加元素
var foo = new Foo { BossId = 1, Deps = { new Dep(), new Dep() } // 注意是集合初始化语法,不是赋值 };
3. 是否能沿用EF6的生成方式?
EF7没有提供直接回退到EF6生成逻辑的官方选项,但可以通过两种方式近似实现:
- 自定义反向工程模板:EF支持使用
--template参数指定自定义T4模板,你可以基于EF6的模板逻辑修改,让生成的代码保留构造函数和set访问器 - 降级脚手架工具版本:不推荐,但如果必须完全匹配EF6结构,可以暂时降级到EF Core 6.x的
Scaffold-DBContext工具,不过这会错过EF7的其他特性
内容的提问来源于stack exchange,提问作者Ian W
相关产品推荐
相关产品推荐

