Reusing a column for a required property with Entity Framework 6.0, Fluent API, and DataAnnotations

≡放荡痞女 提交于 2019-12-11 12:19:08

问题


I have a base class

public class BaseClass
{
    public int Id {get; set;}
}

and two derived classes

public class Foobar: BaseClass
{
    [Required]
    public int Whatever {get; set;}
}

public class Snafu: BaseClass
{
    [Required]
    public int Whatever {get; set;}
}

I'm using Table Per Hierarchy inheritance and trying to cut down on my duplicate columns, so with Fluent API I've mapped them like so:

        modelBuilder.Entity<Foobar>().Property(fb => fb.Whatever).HasColumnName("Whatever");
        modelBuilder.Entity<Snafu>().Property(sf => sf.Whatever).HasColumnName("Whatever");

However, this results in

(137,10) : error 3023: Problem in mapping fragments starting at line 137:Column BaseClass.Whatever in table BaseClass must be mapped: It has no default value and is not nullable.

In EF6 this type of mapping seems to work fine if I take off the [Required] attribute from both subclasses. Adding a [DefaultValue(0)] attribute to both derived classes does not fix the problem.

Any idea how to get these properties to share a column in the database while maintaining their required attribute?


回答1:


This is actually a bug in EF6. In EF5 the scenario used not to work at all (we would throw an exception in the lines of "column names need to be unique"). While in EF6 we did some work to enable it, but apparently we missed the fact that the shared column has to be nullable in the database even if the property is required in the derived types. The latter is because unless the base class is abstract, you need to be able to store an instance of the base type and for any instance of the base type the column should be null.

I have filed the issue in our bug database:

https://entityframework.codeplex.com/workitem/1924

Feel free to vote for it.

As for a workaround, if having an intermediary type is not an option, you can mark the column as nullable explicitly appending a call to .IsOptional() on the entity configurations. This won't give you exactly what you want because for the purpose of EF data validation this call to IsOptional() on the fluent API will override the [Required] data annotation. However, other flavors of data validation, such as MVC's validation will still honor the attribute.

There are other possible workarounds that I haven't tried, maybe if it is acceptable to use TPT and have both derived types have Whatever live in a different table this would work. I believe any approach that relies on setting a default value won't help because the bug is not only about the table schema not being able to hold an instance of the base class, it is also about the EF mapping generated by Code First not being valid.

UPDATE: This will be fixed in Entity Framework version 6.1.0 which is currently available in beta.




回答2:


Introducing another type, which contains the required property shared by the other two accomplishes what you're looking for. The entities then look this:

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
{
}

And the mappings like this:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<BaseIntermediaryClass>().Property(fb => fb.Whatever).HasColumnName("Whatever");
        base.OnModelCreating(modelBuilder);
    }

Full code of working example can be found here: https://gist.github.com/trayburn/7923392



来源:https://stackoverflow.com/questions/20530928/reusing-a-column-for-a-required-property-with-entity-framework-6-0-fluent-api

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!