4

Using Entity Framework 5

So I have a Customer. The Customer could have many Addresses, but at least one. One of the addresses would also be set as the primary address (required). I tried various mappings, but so far I get an error on build or when I seed the database.

Customer:

public class Customer
{
    public int CustomerId { get; set;}
    public String CustomerName { get; set; }
    public int PrimaryAddressId { get; set; }
    public virtual CustomerAddress PrimaryAddress { get; set; }
    public virtual ICollection<CustomerAddress> CustomerAddresses { get; set; }    
}

Address:

public class CustomerAddress : Address
{
    public int CustomerAddressId { get; set; }
    public int CustomerId { get; set; }
    public virtual Customer Customer { get; set; }
}

This part of my mapping is working correctly. It is on CustomerAddress.

        this.HasRequired(c => c.Customer)
            .WithMany(d => d.CustomerAddresses)
            .HasForeignKey(c => c.CustomerId);

But how do I specify the correct mapping for setting PrimaryAddress in Customer? Or is it the wrong approach?

Thanks

EDIT - Using both Arnolds and LueTM's answers:

This code is now working.

Customer:

public class Customer
{
    public int CustomerId { get; set;}
    public String CustomerName { get; set; }
    // public int PrimaryAddressId { get; set; } created in mapping
    public virtual CustomerAddress PrimaryAddress { get; set; }
    public virtual ICollection<CustomerAddress> CustomerAddresses { get; set; }    
}

Address:

public class CustomerAddress : Address
{
    public int CustomerAddressId { get; set; }
    public int CustomerId { get; set; }
    public virtual Customer Customer { get; set; }
}

Customer mapping:

        modelBuilder.Entity<Customer>
            .HasOptional(c => c.PrimaryAddress)
            .WithOptionalDependent().Map(m => m.MapKey("PrimaryAddressId"));

        modelBuilder.Entity<Customer>
            .HasMany(c => c.CustomerAddresses)
            .WithRequired(c => c.Customer)
            .HasForeignKey(c => c.CustomerId)
            .WillCascadeOnDelete(false);

And I use a repository to make sure that a new address is created first, saved, and then also set as primary and saved again. The repository makes sure the primary is "required".

1
  • 2
    Please show the error and the piece of code where it occurs. (By editing your post) Commented Oct 21, 2012 at 9:05

1 Answer 1

1

Since you don't show the exception, I have to assume that you ran into a chicken-egg problem.

If you set PrimaryAddress as a required property EF must have an existing address Id to establish the foreign key (set PrimaryAddressId in Customer). However, since Address requires a Customer you cannot store an address before its customer. And if you try to save an address and a customer in one take, EF can not determine the correct order of inserts, because it needs to insert both objects with the generated Id of the other object.

So either Address or Customer must have an optional foreign key.

I would make Customer.PrimaryAddressId optional:

modelBuilder.Entity<Customer>().HasOptional(c => c.PrimaryAddress)
    .WithOptionalDependent();

Now you can store addresses and assign a primary address to a customer in separeate transactions. But you need business logic to ensure that a Customer always has a primary address.

If you want to save cutomers and addresses in one transaction, an approach could be to add an IsPrimary property (bool) to CustomerAddress and ensure that always exactly one address has true.

Sign up to request clarification or add additional context in comments.

3 Comments

Yes this works. You are right, tried to do save both the address and assign it as primary in one transaction. Guess I was focussed too much about having the primary address "required". To wrap this up: It now generates its own forgeign key. Could I map this to my own "PrimaryAddressId"?
Solved that also. I removed PrimaryAddress id an instead added a mapping for it: this.HasOptional(c => c.PrimaryAddress) .WithOptionalDependent().Map(m => m.MapKey("PrimaryAddressId"));
Ah, didn't see your comment, but glad you found it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.