我有一个基类
public class BaseClass
{
public int Id {get; set;}
}
和两个派生类
public class Foobar: BaseClass
{
[Required]
public int Whatever {get; set;}
}
public class Snafu: BaseClass
{
[Required]
public int Whatever {get; set;}
}
我正在使用Table Per Hierarchy继承并试图减少我的重复列,所以使用Fluent API我就像这样映射它们:
modelBuilder.Entity<Foobar>().Property(fb => fb.Whatever).HasColumnName("Whatever");
modelBuilder.Entity<Snafu>().Property(sf => sf.Whatever).HasColumnName("Whatever");
然而,这会导致
(137,10):错误3023:从第137行开始映射片段时出现问题:列BaseClass.Whatever表中必须映射BaseClass:它没有默认值且不可为空。
在EF6中,如果我从两个子类中取消[Required]
属性,这种类型的映射似乎工作正常。向两个派生类添加[DefaultValue(0)]
属性不能解决问题。
知道如何让这些属性在数据库中共享一个列,同时保持其所需的属性吗?
答案 0 :(得分:2)
这实际上是EF6中的一个错误。在EF5中,场景根本不起作用(我们会在“列名称必须唯一”的行中引发异常)。虽然在EF6中我们做了一些工作来启用它,但显然我们错过了共享列必须在数据库中可以为空的这一事实,即使派生类型中需要该属性。后者是因为除非基类是抽象的,否则您需要能够存储基类型的实例,并且对于基类型的任何实例,该列应该为null。
我已在我们的错误数据库中提交了该问题:
https://entityframework.codeplex.com/workitem/1924
随意投票。
对于变通方法,如果不能选择中间类型,则可以将列标记为可为空,在实体配置上显式附加对.IsOptional()的调用。这并不能完全满足您的需求,因为出于EF数据验证的目的,对Fluent API上的IsOptional()调用将覆盖[Required]数据注释。但是,其他类型的数据验证,例如MVC的验证仍将遵循该属性。
还有其他可能的解决方法,我还没有尝试过,也许如果可以接受使用TPT,并且两个派生类型都有不同的表,这可以使用。我相信任何依赖于设置默认值的方法都无济于事,因为这个bug不仅仅是关于表模式不能保存基类的实例,而且还是关于Code First生成的EF映射不是有效的。
更新:这将在Entity Framework版本6.1.0中修复,该版本目前已在测试版中提供。
答案 1 :(得分:0)
引入另一种类型,其中包含由其他两种共享的必需属性,可以实现您正在寻找的内容。然后实体看起来这样:
public class BaseClass
{
public int Id { get; set; }
}
public abstract class BaseIntermediaryClass : BaseClass
{
[Required]
public int Whatever { get; set; }
}
public class Foobar : BaseIntermediaryClass
{
}
public class Snafu : BaseIntermediaryClass
{
}
这样的映射:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<BaseIntermediaryClass>().Property(fb => fb.Whatever).HasColumnName("Whatever");
base.OnModelCreating(modelBuilder);
}
可在此处找到完整的工作示例代码:https://gist.github.com/trayburn/7923392