0

This is an extension of this question which from what I can tell now works in EF6. However, it seems as though there's a problem when you've got subclasses which have both shared and unshared properties.

Let's say this is my model setup:

public abstract class Document
{
    public int Id { get; set; }
    public string NameOnDocument { get; set; }
}

public class BirthCertificate : Document
{
    public string RegistrationNumber { get; set; }
}

public class Licence : Document
{
    public string LicenceNumber { get; set; }
}

In the database, I want BirthCertificate.RegistrationNumber and Licence.LicenceNumber to share the same column, Number. As such, I'm setting up my model like this:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    // Document - base class
    modelBuilder.Entity<Document>().HasKey(d => d.Id);
    modelBuilder.Entity<Document>()
        .Property(d => d.Id)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    modelBuilder.Entity<Document>()
        .Property(d => d.NameOnDocument)
        .HasColumnName("Name");

    // Birth certificate
    modelBuilder.Entity<Document>().Map<BirthCertificate>(map => 
        map.Property(c => c.RegistrationNumber).HasColumnName("Number"));

    // Licence
    modelBuilder.Entity<Document>().Map<Licence>(map => 
        map.Property(l => l.LicenceNumber).HasColumnName("Number"));
}

When I generate the database, it all looks and works as expected:

DB Model

Now for the issue at hand. Let's say the Licence entity also needs to record an expiry date; so I add that in as follows:

public class Licence : Document
{
    public string LicenceNumber { get; set; }
    public DateTime ExpiryDate { get; set; }
}

Now when I regenerate the database, it looks like this:

enter image description here

What's more, if I try to insert a licence and a birth certificate, I get the following exception:

An unhandled exception of type 'System.Data.Entity.Infrastructure.DbUpdateException' occurred in EntityFramework.dll

Additional information: A value shared across entities or associations is generated in more than one location. Check that mapping does not split an EntityKey to multiple store-generated columns.

I can understand why that exception is raised - because the database is useless.

What have I missed?

Community
  • 1
  • 1
gerrod
  • 6,119
  • 6
  • 33
  • 45

2 Answers2

0

Ok, so it turns out that the problem is simple to solve, and yet so far as I can tell is not documented anywhere. So hopefully this will help someone with the same problem.

Seemingly, the key is that you have to map every property on the derived entity:

modelBuilder.Entity<Document>().Map<Licence>(map =>
{
    map.Property(l => l.LicenceNumber).HasColumnName("Number");
    map.Property(l => l.ExpiryDate).HasColumnName("ExpiryDate");
});

Now my database generates as I would expect and all is right with the world.

gerrod
  • 6,119
  • 6
  • 33
  • 45
0

I'm not sure you are gaining much by putting 2 columns into 1 in the database. I don't think you'll save much space and you can't interrogate the Number of a base Document, even though both documents will have one. Have you considered adding the Number field to the base and using data annotations on overrides - something like this?

public abstract class Document
{
    public int Id { get; set; }
    public string NameOnDocument { get; set; }
    public virtual string Number
}

public class BirthCertificate : Document
{
    [Display(Name="Registration Number"]
    [Required]
    public override string Number { get; set; }
}

public class Licence : Document
{
    [Display(Name="Licence Number"]
    [Required]
    public override string Number { get; set; }
}

EDIT And if all documents aren't numbered:

public abstract class Document
{
    public int Id { get; set; }
    public string NameOnDocument { get; set; }
}

public abstract class NumberedDocument : Document
{
    public virtual string Number
}

public class BirthCertificate : NumberedDocument
{
    [Display(Name="Registration Number"]
    [Required]
    public override string Number { get; set; }
}
Colin
  • 22,328
  • 17
  • 103
  • 197
  • This was a stripped down example of a bigger problem though... There are actually about 6 document types which each share about 5 common properties plus a handful of semi-common properties. Not all documents have a number. Thanks for the suggestion though. – gerrod Nov 13 '14 at 19:39
  • @gerrod edited to show how to use inheritance for "semi-common" properties – Colin Nov 14 '14 at 09:14
  • Thanks; but that's still too simple :-). Its more like - in conjunction with the common properties, my table has seven possibly shared properties - PropertyA, PropertyB.... PropertyG. Document1 has PropertyA, PropertyC. Document2 has PropertyC, PropertyE, PropertyF. Document3 has PropertyB, PropertyE, PropertyG. etc etc Six different document types, each with a different combination of the shared properties. – gerrod Nov 14 '14 at 23:46