使用Entity Framework 6.0,Fluent API和DataAnnotations重用所需属性的列

时间:2013-12-11 22:07:04

标签: entity-framework data-annotations entity-framework-6 fluent-interface

我有一个基类

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)]属性不能解决问题。

知道如何让这些属性在数据库中共享一个列,同时保持其所需的属性吗?

2 个答案:

答案 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