Should I map both sides of bidirectional relations

2019-07-07 04:10发布

问题:

Assume I have the following entity classes:

public class Customer {
  public int Id { get; set; }
  public virtual ICollection<Order> Orders { get; set; }
}

public class Order {
  public int Id { get; set; }
  public virtual Customer Customer { get; set; }
}

How should those be mapped in Entity Framework 6 fluent code-first mapping? I want to be explicit about the mapping and not rely on automatic mapping conventions.

Option 1

Just map the local properties of both classes. That's how I would do it in Fluent NHibernate.

public class CustomerMap : EntityTypeConfiguration<Customer> {
  public CustomerMap() {
    HasMany(x => x.Orders);
  }
}

public class OrderMap : EntityTypeConfiguration<Order> {
  public OrderMap() {
    HasRequired(x => x.Customer);
  }
}

Option 2

Map both sides of the relationship in both classes.

public class CustomerMap : EntityTypeConfiguration<Customer> {
  public CustomerMap() {
    HasMany(x => x.Orders).WithRequired(x => x.Customer);
  }
}

public class OrderMap : EntityTypeConfiguration<Order> {
  public OrderMap() {
    HasRequired(x => x.Customer).WithMany(x => x.Orders);
  }
}

Option 3

Map both sides of the relation, but only in one of the classes. The code would be similar to option 2, just one of the two constructors would be empty.


Is there any difference between those options? If yes, please also explain why I should or shouldn't use a specific option.

回答1:

I would go for option 3.

In option 1 you can forget to map the inverse end of an association. In this simple example it's clear that Order.Customer and Customer.Orders are two ends of the same association. When things get more complex, this isn't always obvious. Also, it is redundant code.

In option 2 you could have conflicting mappings. For instance when you have...

HasOptional(x => x.Customer).WithMany(x => x.Orders);

...in OrderMap, you will get a runtime exception telling you that both mappings don't match. And again, it is redundant code.

So option 3 is DRY and safe. The only issue is that it's a bit arbitrary where to configure the mappings. I tend to adhere to mapping children in their parent's mapping.

One more comment. You may want to add a primitive property CustomerId in Order. The mapping would look like:

public class CustomerMap : EntityTypeConfiguration<Customer>
{
    public CustomerMap()
    {
        HasMany(x => x.Orders).WithRequired(x => x.Customer)
                              .HasForeignKey(o => o.CustomerId);
    }
}

Now you have full control over both ends of the association and the foreign key name to be used. Besides that, there are some advantages of these foreign key associations as opposed to independent associations (without a primitive foreign key property). For instance, the ability to establish an association without having to fetch the parent object from the database. You can just by set an Id value.