3

I'm wondering what's the best way to handle default values for relationships when making models. (Specifically EF4)

For example, my Organization has a default Contact and I was wondering which one was the best approach. I got these two options (or any other anyone suggests if better)

Using Relationship:

public  class Contact
{
    public int Id { get; set; }
    public string FirstName { get; set; }
}

public  class Organization
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Contact> Contacts { get; set; }
    //Use a relationship for the default contact?
    public Contact DefaultContact { get; set; }
}

Using Value:

public  class Contact
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    //Use value?
    public boolean IsDefault { get; set; }
}

public  class Organization
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Contact> Contacts { get; set; }
}
LECHIP
  • 81
  • 1
  • 8
  • I'd go with with option 2 for the sake of reduced complexity. Not that option one is overly complex, but having a bool property seems more straight-forward and easier to manage than a custom type property – Forty-Two Jul 31 '12 at 15:29
  • This question reaches well beyond EF 4 model first and even OR/M, since it has implications in any OR/M and even at the DB level. I would consider editing the question with that in mind, it will reach a much wider audience. – Marc L. Jul 31 '12 at 16:20
  • I do agree, but since the specific technology is EF I'll keep those tags to see how other people have solved this kind of scenario before using the same tools. Tnx Marc. – LECHIP Jul 31 '12 at 16:23

1 Answers1

2

I'd go with Option 1. While 2 is definitely easier to implement, it doesn't enforce rules such as "There cannot be 2 default contacts". I end up with something like the following:

public class Organization {
    // ...
    public virtual ICollection<Contact> { get;set; }
    [ForeignKey("DefaultContactId")]
    public Contact DefaultContact { get;set; }
    public int? DefaultContactId { get;set; }
}

There's a limitation of this approach - it doesn't work nested deletes (see this question for more details). Because of this, you need to disable CascadeOnDelete for the 1-to-many relationship:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    modelBuilder.Entity<Contact>().HasRequired(co => co.Organization).WithMany().WillCascadeOnDelete(false);
}

(Code done without testing, but should work)

The other problem with this is that it's not possible to add the Default Contact at the same time as you're adding the organization, as EF can't figure out the correct order of statements. You need to call .SaveChanges between each. You can still use a TransactionScope to overcome this, but it's not clean:

using (var ts = new TransactionScope()) 
{
    Organization org = new Organization
    {
        // ...
        Contacts = new Collection<Contact>()
    }
    org.Contacts = new Contact() {};

    orgRepo.SaveChanges();

    // Now wire up the default contact
    org.DefaultContact = org.Contacts.First();
    orgRepo.SaveChanges();
}
Community
  • 1
  • 1
Richard
  • 29,854
  • 11
  • 77
  • 120