1

Closed

6.1.3 Inheritance Bug (6.1.1 works fine)

description

With this model
public class Class1
{
    [Key]
    public int Id { get; set; }

    public virtual string MyProperty { get; set; }
    // other properties 
}

public class Class3 : Class1
{
    [Required]
    public override string MyProperty { get; set; }

    public string Class3Prop { get; set; }
}


public class TestContext : DbContext
{
    public TestContext(DbConnection connection) : base(connection, true) { }

    public DbSet<Class1> C1s { get; set; }
    public DbSet<Class3> C3s { get; set; }


}
I expect that Class3.MyProperty is required and Class1.MyProperty is not required.

With EF 6.1.1 everything's working fine (in SQL Server the MyProperty field is nullable).
With EF 6.1.3 both Class3.MyProperty and Class1.MyProperty are interpreted as Required (and in SQL Server MyProperty field is not nullable)
Closed Jul 29, 2016 at 10:00 PM by RoMiller
We are transitioning this project to GitHub (https://github.com/aspnet/EntityFramework6). This issue has been ported to the new issue tracker - https://github.com/aspnet/EntityFramework6/issues/18. We will no longer be monitoring this issue tracker for comments, so please do not reply here.

comments

RoMiller wrote Aug 18, 2015 at 5:31 PM

Hey,

In EF these two properties in CLR are really the same property in the metadata model (so they can't differ in requiredness as far as EF is concerned). It seems like in past releases we would not pick up the attribute on the overridden property but in 6.1.3 we started picking it up.

If you use the Fluent API to make it optional does it give the behavior you want?
modelBuilder.Entity<Class1>() 
    .Property(t => t.MyProperty ) 
    .IsRequired(false);
~Rowan

bubibubi wrote Aug 21, 2015 at 1:36 PM

Hi Rowan,
actually 6.1.1 worked in a little different way or at least, if there is only one metadata model entry for MyProperty, the MyProperty is configured in the right way in 6.1.1 (on DB MyProperty nullability should be the less restrictive and so it is in EF 6.1.1) and 6.1.1 does not use the metadata model to perform checks on SaveChanges (it checks for MyProperty null via code only on Class3 because on Class1 MyProperty can be nullable).

Look at this test.
    public static void Run(DbConnection connection)
    {
        using (TestContext db = new TestContext(connection))
        {
            // MyProperty not specified
            db.C1s.Add(new Class1());
            db.SaveChanges();

            // MyProperty not specified
            db.C3s.Add(new Class3());
            db.SaveChanges();
        }
    }
With EF 6.1.1
The field on database was created as not nullable.
The piece of code above raised an exception on the second SaveChanges (probably it is an EF check not a DBMS check) and is right becouse in Class3 MyProperty is Required.
The first SaveChanges worked fine because MyProperty in Class1 is not required.

So, about your answer, you picked up the attributes in the right way (Class1.MyProperty not required, Class3.MyProperty required). Probably in the metadata model you get the less restrictive but then, when you check entities on SaveChanges, you get the proper one.

With EF 6.1.3
The exception is raised on the first SaveChanges.


About the test you requested (configure everything via fluent interface) I think that in EF 6.x there is not an IsRequired overload with 1 parameter (same as RequiredAttribute).

RoMiller wrote Aug 28, 2015 at 5:56 PM

@bubibubi - you are right, the API is actually IsOptional() in EF6.

We will re-discuss in triage based on the info you provided about validating the derived type and not the base type.

RoMiller wrote Aug 28, 2015 at 6:54 PM

@Andriy - can you dig into this a little, we remember making changes around this in EF6.1.3 but we're not sure what our intention was.

AndriySvyryd wrote Dec 14, 2016 at 7:59 PM

in 6.1.1 most facets configured on the derived type were ignored, this was fixed in 6.1.2. The reported behavior is by design, the workaround is to configure the property using Fluent API:
modelBuilder.Entity<Class3>().Property(c => c.MyProperty).IsOptional();