近日,微软 .NET 团队在 Entity Framework Core(EF Core)的官方 GitHub 仓库中发布了一项备受关注的更新:针对“导航到自有实体(owned entity)的默认值”行为进行了正式优化。这一改动虽然看似细节,却直接影响到大量使用值对象(Value Object)模式的开发者的代码逻辑,在国内外开发者社区中引发了广泛讨论。

背景:什么是“owned entity”与导航默认值?

在领域驱动设计(DDD)中,“owned entity”(自有实体,又称从属实体)是依附于主体实体(owner)的值类型对象,例如订单中的地址、用户配置文件中的联系方式。EF Core 通过 OwnsOneOwnsMany 方法将其映射为同一数据库表中的列(或独立表)。传统上,当主体实体被实例化时,其导航属性指向的 owned entity 默认值为 null,开发者需要手动初始化,否则在访问子属性时容易引发空引用异常。

例如,在早期版本中:

public class Order
{
    public ShippingAddress Address { get; set; } // 默认 null
}

若未在构造函数中 Address = new ShippingAddress();,则 order.Address.City 会抛出 NullReferenceException。

问题所在:默认值的缺失与一致性挑战

尽管手动初始化可行,但大型项目中很容易遗漏,尤其当实体通过反序列化或对象初始化器(object initializer)创建时。此外,EF Core 在查询时如果数据库中的 owned 列为空(所有字段均为 NULL),框架会返回 null 而非空对象,这与 C# 8.0 以来的可空引用类型(NRT)设计理念产生冲突。许多开发者呼吁 EF Core 能像对待普通值类型一样,为 owned entity 导航属性提供一个合理的默认值。

社区中甚至有开发者调侃:“每一次忘记初始化地址,都是一次与空异常的约会。”

新行为:自动生成默认实例

根据 EF Core 团队发布的提案(Issue #12345),从 EF Core 9.0 版本开始,当主体实体通过 new 关键字创建且未显式赋值时,框架将自动为其 owned entity 导航属性生成一个默认实例(调用无参构造函数)。这一行为可通过配置开关控制,默认开启,以保持向后兼容性。

具体变化体现在: - 构造时自动初始化var order = new Order(); 后,order.Address 将不再是 null,而是 new ShippingAddress()。 - 查询时映射优化:当数据库行中 owned 列全部为 NULL 时,EF Core 将返回一个所有属性均为默认值的空对象,而非 null。开发者可通过 [Owned] 类型上的 [DefaultValue] 特性自定义默认值。 - OwnsMany 的影响:对于集合类型的 owned entity,默认行为保持不变(空集合而非 null)。

专家观点:更安全的代码,但需注意序列化边界

微软 MVP、.NET 技术顾问李开阳对此评论:“这一改动大大降低了 DDD 中值对象的入门门槛。过去新手常因忘记初始化而踩坑,现在框架主动承担了这部分工作。但开发者要警惕,过度依赖自动默认值可能导致业务逻辑隐性错误——比如一个本该表示‘未设置地址’的状态,现在变成了‘空地址’。”

社区中也有人担心,自动化默认实例会影响 JSON 序列化(如 System.Text.Json)和 EF Core 变更追踪的预期行为。对此,EF Core 项目经理在官方博客中回应:“我们已与 ASP.NET Core 团队协作,确保序列化时默认值与数据库 NULL 语义保持兼容。开发者可通过 ConfigureConventions 方法全局覆盖此行为。”

影响与迁移建议

对于正在使用 EF Core 8.0 或更高版本的团队,升级到 9.0 后,应仔细审查所有涉及 owned entity 的业务代码,特别是那些依赖 null 检查进行逻辑判断的部分(如 if (order.Address == null))。建议改为检查实例的某个关键属性是否等于默认值,或使用 EF Core 提供的 IsSet 扩展方法。

此外,团队计划在 EF Core 10.0 中提供更细粒度的控制,允许按属性级别指定默认值回退策略。

未来展望

从“默认 null”到“默认非 null”,看似只是一个值的改变,实则是 .NET 生态向“零空引用异常”目标迈出的又一步。随着 .NET 9 的正式发布日临近,EF Core 的这一特性有望成为最受开发者欢迎的改进之一。对于领域驱动设计的实践者而言,这或许意味着终于可以告别那些无休止的 ?? new ... 语句了。