EF TPH继承类多构造函数问题:LocationId非空时强制用指定构造函数
问题:TPH继承类中强制BlogLocationAction在LocationId非空时使用指定构造函数
我采用EF的Table-Per-Hierarchy(TPH)配置继承类,已生成带鉴别器列和自定义可空字段的单表。现有代码如下:
public abstract class ActionBase { /// ctor1 public ActionBase(long issueId) { IssueId = issueId; Url = $"https://mypath.com/{issueId}"; } /// ctor2 public ActionBase(long issueId, long locationId) { IssueId = issueId; LocationId = locationId; Url = $"https://mypath.com/{issueId}/{locationId}"; } public long IssueId {get;set;} public long? LocationId {get;set;} public string Url { get; } } public class BlogAction: ActionBase { /// ctor1 public BlogAction(long issueId): base(issueId) {} } public class LocationAction: ActionBase { /// ctor1 public LocationAction(long issueId, long locationId): base(issueId, locationId) {} } public class BlogLocationAction: ActionBase { /// ctor1 public BlogLocationAction(long issueId): base(issueId) {} /// ctor2 public BlogLocationAction(long issueId, long locationId): base(issueId, locationId) {} }
现在创建BlogLocationAction实例时总是调用第一个构造函数,需要实现当LocationId非空时必须使用第二个构造函数的约束。
解决方案
1. 限制构造函数访问权限,添加实例验证
将无locationId的构造函数设为protected,仅允许EF或子类调用,同时在类中添加验证逻辑确保LocationId的状态与构造方式匹配:
public class BlogLocationAction: ActionBase { // 仅允许EF或子类调用,外部代码无法直接使用 protected BlogLocationAction(long issueId): base(issueId) { // 可选:手动实例化时验证状态(可通过标记字段区分EF加载和手动创建) } public BlogLocationAction(long issueId, long locationId): base(issueId, locationId) {} // 可选:添加状态验证方法,在业务逻辑中调用 public void ValidateInstance() { if (LocationId.HasValue) { // 可通过构造时的标记字段判断是否使用了错误构造函数,抛出异常 throw new InvalidOperationException("当LocationId非空时,必须使用带locationId参数的构造函数"); } } }
2. 使用工厂方法完全控制实例化逻辑
隐藏所有构造函数(设为private或protected),通过静态工厂方法创建实例,从根源上约束调用逻辑:
public class BlogLocationAction: ActionBase { private BlogLocationAction(long issueId): base(issueId) {} private BlogLocationAction(long issueId, long locationId): base(issueId, locationId) {} // 无LocationId时的创建入口 public static BlogLocationAction CreateForBlog(long issueId) { return new BlogLocationAction(issueId); } // 必须传入LocationId时的创建入口 public static BlogLocationAction CreateForLocation(long issueId, long locationId) { return new BlogLocationAction(issueId, locationId); } // 可选:EF加载需要的无参构造函数(根据EF版本调整) protected BlogLocationAction() {} }
外部代码只能通过指定的工厂方法创建对象,彻底避免错误调用构造函数的情况。
3. EF构造函数映射配置
如果使用EF Core,可在OnModelCreating中指定EF加载实体时使用的构造函数,避免EF错误调用构造函数:
protected override void OnModelCreating(ModelBuilder modelBuilder) { // 指定EF加载BlogLocationAction时优先使用带两个参数的构造函数 modelBuilder.Entity<BlogLocationAction>() .HasConstructor(new[] { typeof(long), typeof(long) }); }
内容的提问来源于stack exchange,提问作者Mark Cooper
相关产品推荐
相关产品推荐

