Monday, 1 February 2016

Making Complex Types Useful with Entity Framework 6 Custom Configurations


Complex Types let you reuse structures in your database design. But unless you've been very lucky around the names in your database, you probably couldn't use Complex Types -- until Entity Framework 6, that is.
Unfortunately, because of the conventions of EF, unless you were creating a brand new table or had been very lucky with your naming conventions in the past, you couldn't use Complex Types. So no matter what the object-oriented benefits you'd get from using Complex Types, the reality of your legacy databases wouldn't let you grab those benefits -- until EF6 came along with its support for custom conventions and configurations.
Creating Complex Type
EF Complex Types let you define repeating structures in your database's tables as classes, then use those classes in your entities as nested classes (see my tip on the usefulness of nested classes outside of EF). For address information, you would begin by creating a class that defines the structure of your address information. EF has rules for discovering Complex Types, but adding the ComplexType attribute to your Address class reduces the chances of EF guessing wrong:
[ComplexType]
public class Address
{
  public string City {get; set;}
  public string Street { get; set; }
  public string StateOrProvince { get; set; }
  public string Country { get; set; }
}

There are some restrictions around Complex Types: they can't have a primary key (you can describe them as value objects in some object classification schemes), inherit from other classes, contain navigation properties, be collections, and (most important) you can't use them as standalone entities. But what you can do is use your Complex Type as a nested class in other entities. Here's a Customer entity that uses my Address Complex Type to define two different addresses:
public partial class Customer
{
  public int Id { get; set; }
  public string FirstName { get; set; }
  public string LastName { get; set; }
  public Address ShippingAddress { get; set; }
  public Address BillingAddress { get; set; }
}
The Address class is still a class, and like any other class, it needs to be instantiated. If no Address data is provided for the class, EF won't instantiate the Address class, which could result in it throwing null value errors. To prevent that, you should add a constructor to your entity class holding the Complex Type and initialize the properties that use the Complex Type. For my Customer class, that code would look like this:
public partial class Customer
{
  public Customer()
  {
    this.BillingAddress = new Address();
    this.ShippingAddress = new Address();
  }
  public int Id { get; set; }
  …
Configuring Entity Framework to Your Database
When you go to use Complex Type with an existing database, you'll probably find your existing database won't support it. EF naming conventions mean the database is going to expect the Customer table corresponding to my Customer entity to have columns with the names ShippingAddress_City, ShippingAddress_Street, BillingAddress_City, BillingAddress_Street and so on. If, for instance, my Customer table has columns called ShipAddress and BillToStreetAddress, I'll be out of luck.
That is, until the EF6 custom conventions and configurations come along to give you more control over how your EF model ties to your database. You just need a little bit of fluent code in your DbContext OnModelCreating method to resolve this problem.
The first step is to pick the class your convention will apply to, using the ModelBuilder Types collection. In my example, I want to control my Customer entity class, so I specify it in the Types collection:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  modelBuilder.Types<Customer>()
The next step is to specify what you want to configure on that type by calling the Configure method. The Configure method accepts a lambda expression and passes that expression a ConventionTypeConfiguration object. You use the ConventionTypeConfiguration object to tell EF what you want to configure. I want to override the configuration for one of my entity's properties, so I use the Property method on the ConventionTypeConfiguration object:
modelBuilder.Types<Customer>()
            .Configure(ctc => ctc.Property( ...
The Property method also accepts a lambda expression that allows you to specify which property on the entity you want to configure (this lambda expression is passed the entity's description). I want to configure the State property within the Customer ShippingAddress, so I write this code:
modelBuilder.Types<Customer>()
            .Configure(ctc => ctc.Property(cust => cust.ShippingAddress.Street) ...
Finally, you supply the configuration for the property. In this case, I tell EF what the column's name really is, using the HasColumnName method:
modelBuilder.Types<Customer>()
            .Configure(ctc => ctc.Property(cust => cust.ShippingAddress.Street).HasColumnName("ShipAddress"));
I'll need another configuration to handle my BillToStreetAddress:
modelBuilder.Types<Customer>()
            .Configure(ctc => ctc.Property(cust => cust.BillingAddress.Street).HasColumnName("BillToStreetAddress"));
This new ability to configure EF frees me to design my entities in an object-oriented way that solves my business problems. Once I've got the objects I want, I can do what's necessary to tie those well-designed entity objects to my database. And that's the right thing to do.

No comments:

Post a Comment

Angular Tutorial (Update to Angular 7)

As Angular 7 has just been released a few days ago. This tutorial is updated to show you how to create an Angular 7 project and the new fe...