Question

I have created three different classes and one base class which store different types of addresses. Base class is postal address which has relation to User (to whom is current address attached) and Post, which contain information about zip code and city.

public class PostalAddress
{
    public virtual User User { get; set; }
    public DateTime LastUsed { get; private set; }

    public string OrientationNumber { get; set; }
    public int UserId { get; set; }

    public int PostId { get; set; }

    public int Id { get; set; }
    public virtual Post Post { get; private set; }

    public string Street  { get; set; }
}

public class Post
{
    public Post()
    {
        InvoicingAddresses = new List<InvoicingAddress>();
        ShippingAddresses = new List<ShippingAddress>();
        UserAddresses = new List<UserAddress>();
    }

    public virtual City City { get; set; }
    public virtual ICollection<InvoicingAddress> InvoicingAddresses { get; private set; }
    public virtual ICollection<ShippingAddress> ShippingAddresses { get; private set; }
    public virtual ICollection<UserAddress> UserAddresses { get; private set; }
    public int CityId { get; set; }
    public int Id { get; set; }
    public string ZipCode { get; set; }   
}

Class PostalAddress is mapped using class PostalAddressMap

public class PostalAddressMap : EntityTypeConfiguration<PostalAddress>
{
    public PostalAddressMap()
    {
        // Primary Key
        HasKey(t => t.Id);

        // Properties
        // Table & Column Mappings
        ToTable("PostalAddress");
        Property(t => t.Id).HasColumnName("Id");
        Property(t => t.LastUsed).HasColumnName("LastUsed").HasColumnType("datetime2");
        Property(t => t.OrientationNumber).HasColumnName("OrientationNumber");
        Property(t => t.UserId).HasColumnName("UserId");
        Property(t => t.PostId).HasColumnName("PostId");
        Property(t => t.Street).HasColumnName("Street");            

        // Relationships
        HasRequired(t => t.Post).WithMany(t => t.InvoicingAddresses).HasForeignKey(d => d.PostId);
        HasRequired(t => t.User)
            .WithMany(t => t.UserAddressess)
            .HasForeignKey(d => d.UserId);


    }
}

Classes InvoicingAddress, ShippingAddress and UserAddress are inherited from PostalAddress class using Table per hierarchy approach. If I want to set relationships using line

        HasRequired(t => t.Post).WithMany(t => t.InvoicingAddresses).HasForeignKey(d => d.PostId);

I receive compiler error Cannot implicitly convert type 'System.Collections.Generic.ICollection<InvoicingAddress>' to 'System.Collections.Generic.ICollection<PostalAddress>'. An explicit conversion exists (are you missing a cast?)

Please, can you help me how can I set foreign key between PostalAddress child classes and other TPT types?

Thank you for any helpful answer.

Was it helpful?

Solution

You must either move the PostId and Post properties from the base class PostalAddress to the derived classes InvoicingAddress, etc...

public class InvoicingAddress : PostalAddress
{
    //...
    public int PostId { get; set; }
    public virtual Post Post { get; private set; }
}

...and then use a mapping for the derived classes:

public class InvoicingAddressMap : EntityTypeConfiguration<InvoicingAddress>
{
    public InvoicingAddressMap()
    {
        HasRequired(t => t.Post)
            .WithMany(t => t.InvoicingAddresses)
            .HasForeignKey(d => d.PostId);
    }
}

Or you must use a single collection in Post for the base class:

public virtual ICollection<PostalAddress> Addresses { get; private set; }

Then you can use your original mapping.

The downside of the latter approach is that when you use eager or lazy loading all PostalAddresses will be loaded and you can't control which type of address you want to load. After the addresses have been loaded you could filter by type in memory though:

var invoicingAddresses = post.Addresses.OfType<InvoicingAddress>();

With explicit loading you can filter too:

var post = context.Posts.Single(p => p.Id == 1);
context.Entry(post).Collection(p => p.Addresses).Query()
    .OfType<InvoicingAddress>().Load();

...which would populate the Addresses collection with the InvoicingAddresses only.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top