Be Careful with Fluent Configuration and Entities Using Complex Types

I ran into a very odd issue today. I was attempting to add a new record and was getting database integrity exceptions because a particular set of columns were configured as NOT NULL on the table. The problem? They were strings and RequiredAttribute was nowhere to be found. Strings are nullable by default. The only way to make them not nullable at the database is to configure them as required, which I was not doing. After a little investigation (and much cursing and gnashing of teeth), I finally stumbled upon the issue: unexpected behavior arising from applying fluent configuration to properties from a complex type. Let's look at some example code:

[ComplexType]
public class Address
{
    public string Street { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
}

public class Place
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Address Address { get; set; }
}

As you can see, nothing is decorated with [Required] here, and so far, there's no fluent configuration involved in setting anything as required. If you migrate this, all the columns from Address will be nullable.

Now, let's add another class:

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

    [ForeignKey("User")]
    public UserId { get; set; }
    public virtual User User { get; set; }

    public Address Address { get; set; }
}

But, here, I want some of the address fields to be required. If I added [Required] to the properties on the complex type, it would affect Place as well, so I had the bright idea of using fluent config:

public class UserAddressMapping : EntityTypeConfiguration<UserAddress>
{
    public UserAddressMapping()
    {
        Property(m => m.Address.Street).IsRequired();
        ...
    }
}

Only that apparently doesn't work. The assumption was that since this is fluent config for UserAddress that this would only affect UserAddress. Seems pretty logical, I think. However, instead, it's actually applying the required constraint to the Address complex type, and as a result, all other entities using that complex type. It's exactly the same as having decorated the property with [Required] directly on the complex type, but worse, because it's config for some seemingly unrelated class, it's not obvious where or how this constraint is being applied.

Now, I don't know whether this is intended behavior in Entity Framework or a bug. It feels like a bug, because allowing config for one particular entity to have wide-ranging consequences for other entities seems extremely dangerous. It's also worth mentioning that this behavior occurs in Entity Framework 6. I haven't tried it in version 7, and applying fluent config on a complex type property from the entity that utilizes it wasn't even supported prior to version 6. I suppose one could say it's still not supported, because given the non-obvious behavior, it's frankly useless. Just a public service announcement here: be very careful when applying fluent configuration to entities utilizing complex types. You might be affecting more than just that entity.

comments powered by Disqus