0

I have an Organization entity and Address entity in a .NET Core 2.1.12 setup. An organization has 0 or 1 PrimaryAddress and 0 or 1 BillingAddress, both of type Address. I'm struggling with multiple different approaches I have tried.

These two links both looked promising, but both are for ICollection<> of an entity (as if there were multiple possible billing addresses rather than 0 or 1), not a single entity, and I can't seem to get past that. I have also tried the [InverseProperty] attribute with no luck.

https://www.entityframeworktutorial.net/code-first/inverseproperty-dataannotations-attribute-in-code-first.aspx Entity Framework Code First - two Foreign Keys from same table

One side note thought I had about this was also that the plural of Address is Addresses ("ES" not just an "S") and not sure if that messes with any kind of built in functionality based on naming conventions.

public class Organization : Trackable
{
    [Required]
    public int OrganizationId { get; set; }

    [Required]
    [MaxLength(100)]
    public string OrganizationName { get; set; }

    // other scalar properties

    public int PrimaryAddressId { get; set; }
    public virtual Address PrimaryAddress { get; set; }

    public int BillingAddressId { get; set; }
    public virtual Address BillingAddress { get; set; }
}

public class Address : Trackable
{
    [Required]
    public int AddressId { get; set; }

    [Required]
    [MaxLength(100)]
    public string Street1 { get; set; }

    // other scalar properties
}

Can anyone provide some insight into the best way to set this up?

Thanks!

mateoc15
  • 509
  • 5
  • 18

1 Answers1

0

This looks like you have been relying on convention for EF to resolve the schema for your model. This works for basic stuff, but the magic doesn't cover everything. To have two references to the same entity type you need to explicitly configure the FK:

[ForeignKey("PrimaryAddress")]
public int PrimaryAddressId { get; set; }
public virtual Address PrimaryAddress { get; set; }

[ForeignKey("BillingAddress")]
public int BillingAddressId { get; set; }
public virtual Address BillingAddress { get; set; }

To make the Address table "Addresses" (I believe EF may do this automatically, but to be safe, explicit configuration)

[Table("Addresses")]
public class Address
{
   // ...
}

It's worth familiarizing yourself with explicit configuration for entities and such using the attributes and/or the IEntityTypeConfiguration<T> (EF Core) or OnModelCreating DbContext event. I tend to just default to explicit configuration to avoid surprises when EF goes and gets unhappy with something.

Explicit config is very useful when wiring up EF to an existing database that has unfriendly naming conventions like ALL_CAPS_UNDERSCORE_FTW. This allows you to make your code a lot more readable without those monstrosities for property names. :)

Steve Py
  • 26,149
  • 3
  • 25
  • 43
  • Excellent, that really helps. Thank you Steve. I have read through the Fluent API documentation and I just can't wrap my head around some of it for some reason. I now have the dreaded "may cause cycles or multiple cascade paths", which conceptually I get. I just want to use a set-to-null option for this. Intellisense seems to be failing me in VS Code. What am I missing here? modelBuilder.Entity() .HasOne(a => a.PrimaryAddress) .WithMany(???) .OnDelete(DeleteBehavior.SetNull); – mateoc15 Sep 15 '19 at 23:47
  • Since Addresses won't have a reference to their Organizations, the .WithMany() call would have no parameters. You shouldn't really need the OnDelete() in most cases, it will likely throw an exception if you tried deleting an address that was referenced by one or more Organizations through Primary/Billing addresses. For setting #null to work, the FK fields (PrimaryAddressId/BillingAddressId) would need to be nullable. (int?) Deleting an organization will not delete the addresses since they don't directly tie to a Org. You'd need to manage those manually. – Steve Py Sep 15 '19 at 23:53
  • I had tried the int? to make PrimaryAddressId and BillingAddressId nullable, hoping to avoid that error. dotnet ef database update still is failing. Here's the full error: "Introducing FOREIGN KEY constraint 'FK_Organizations_Address_PrimaryAddressId' on table 'Organizations' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints." – mateoc15 Sep 17 '19 at 01:40