Entity Framework 7 with a Class that Contains Collections

In order to explore entity framework a bit more I took my existing contact class and broke it down to the following.

public class Contact
{
    public int ContactId { get; set; }
    public string UserId { get; set; }
    [Required]
    [StringLength(200, MinimumLength = 0)]
    public string Name { get; set; }
    public List<ContactAddress> Addresses { get; set; }
    public List<ContactPhone> Phones { get; set; }
    public List<ContactEmailAddress> EmailAddresses { get; set; } 
}

The following is the new email address model from it you will get the idea of what the address and phone models contain.

public class ContactEmailAddress
{
    public int ContactId { get; set; }
    public int EmailId { get; set; }
    [EmailAddress]
    public string Email { get; set; }

    public Contact Contact { get; set; }
}

Next up was the DbContext which got a new DbSet, set up keys and relationships between the models. Again I am limiting the example to contact and email addresses but the same concepts apply to the other related models.

public class ContactsDbContext : DbContext
{
    private static bool _created;
    public DbSet<Contact> Contacts { get; set; }
    public DbSet<ContactEmailAddress> ContactEmailAddresses { get; set; }

    public ContactsDbContext()
    {
        if (!_created)
        {
            Database.AsRelational().ApplyMigrations();
            _created = true;
        }
    }

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.Entity<Contact>().Key(c => c.ContactId);

        builder.Entity<ContactEmailAddress>(entity =>
        {
            entity.Key(e => new { e.ContactId, e.EmailId });
            entity.Reference(e => e.Contact)
                  .InverseCollection(c => c.EmailAddresses)
                  .ForeignKey(e => e.ContactId);
        });
    }
}

In the above code the override of OnModelCreating establishes the connection between the models. InverseCollection establishes that there can be multiple email addresses for a single contact. I am not using it, but InverseReference would establish a one to one relationship.

After all the model changes are complete add a new migration from the command line using the following command. Then apply the migration by using the apply command or running the application if your application auto applies migrations.

dnx . ef migration add ContactsSplit --context ContactsDbContext

Here is an example query using this new class setup that include data from the contacts table and joins the email address table to get the related email addresses.

var contacts = from c in _db.Contacts
                         .Include(c => c.EmailAddresses)
               where c.UserId == User.GetUserId()
               select c;

The include statement above is what make entity framework eagerly load the data from the email address table. The default behavior would not load the email addresses. Also note that include statements can be chained as needed.

The following query is the same as above without eagerly loading the contact’s email addresses.

var contacts = from c in _db.Contacts
               where c.UserId == User.GetUserId()
               select c;

At this point the EmailAddresses property of the returned contacts would be null. To load the email addresses for a particular contact the following can be used.

_db.ContactEmailAddresses.Where(e => e.ContactId == contact.ContactId).Load();

This post on stackoverflow has a great set of tips related to collections with entity framework 7.

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.