Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Keys on Derived Type...But It's Not

I'm new to EF7, and am running into a strange problem. I have this class:

public class Site
{
    public int ID { get; set; }
    public string Title { get; set; }
    public string HouseNumber { get; set; }
    public string StreetName { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string Zipcode { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public DataSource Source { get; set; }

    public object Parameters
    {
        get
        {
            switch( Source )
            {
                case DataSource.StealthStats:
                    return JsonConvert.DeserializeObject<StealthStatsParameters>( JSONParameters );

                default:
                    throw new Exception( "Site::Parameters::get() - Unhandled DataSource " + Source.ToString() );
            }
        }

        set
        {
            switch( Source )
            {
                case DataSource.StealthStats:
                    JSONParameters = JsonConvert.SerializeObject( value );
                    break;

                default:
                    throw new Exception( "Site::Parameters::set() - Unhandled DataSource " + Source.ToString() );
            }
        }
    }

    protected string JSONParameters { get; set; }

    public List<Observation> Observations { get; set; }
}

and this logic in the context's OnModelCreating():

protected override void OnModelCreating(ModelBuilder builder)
{
    base.OnModelCreating(builder);
    // Customize the ASP.NET Identity model and override the defaults if needed.
    // For example, you can rename the ASP.NET Identity table names and more.
    // Add your customizations after calling base.OnModelCreating(builder);

    builder.Entity<Site>()
        .HasKey( t => t.ID );
    builder.Entity<Site>()
        .Property( s => s.City )
        .IsRequired();
    builder.Entity<Site>()
        .Property( s => s.HouseNumber )
        .IsRequired();
    builder.Entity<Site>()
        .Property( s => s.Source )
        .IsRequired();
    builder.Entity<Site>()
        .Property( s => s.State )
        .IsRequired()
        .HasMaxLength( 2 );
    builder.Entity<Site>()
        .Property( s => s.StreetName )
        .IsRequired();
    builder.Entity<Site>()
        .Property( s => s.Title )
        .IsRequired();
    builder.Entity<Site>()
        .Property( s => s.Zipcode )
        .IsRequired()
        .HasMaxLength( 10 );

    builder.Entity<Observation>()
        .HasKey( t => new { t.SiteID, t.TimeStamp } );
    builder.Entity<Observation>()
        .HasOne( o => o.Site )
        .WithMany( s => s.Observations );

}

But when I run dnx ef migrations add, I get this error message:

The derived type 'SpeedView.Models.Site' cannot have keys other than those declared on the root type.

Yet so far as I can see, Site is not derived from anything.

BTW, here's the definition for the class Observation, in case that's important:

public class Observation
{
    public int SiteID { get; set; }

    public DateTime TimeStamp { get; set; }

    public int MPH { get; set; }

    public int VehicleCount { get; set; }

    public virtual Site Site { get; set; }
}

As an aside, if anyone can recommend some links to good tutorials and explanations of EF7 I would appreciate that. I'm finding the learning curve on it, after working with EF for years, to be very steep, and the stuff I've found online isn't terribly helpful.


1 Answers

I posted this over on the github site for Entity Framework, and Smit Patel quickly answered the question and explained what's happening. You can read what he wrote at https://github.com/aspnet/EntityFramework/issues/5151. Thanx, Patel!

The short version is this: object properties in an EF class cause the code that creates EF migrations to include the object type in the overall scan. Since all classes are descended from object, and object has no inherent primary key, all classes run afoul of the "only root classes can have keys defined" limitation.

A solution is to not map an object property to the underlying database (that's what I did).

In doing so, I discovered that it looks like you have to signal your intent by applying the [NotMapped] annotation to the property. The fluent API approach:

builder.Entity<T>().Ignore(t => t.PropertyToIgnore);

doesn't work.