Entity Framework 4.1: Complex Types (4)

This is part of a series of blog post about Entity Framework 4.1.  The past blog entries are:

In this article, I’ll cover the complex types.

By default, EF 4.1 is mapping classes to table.  That is convenient but sometimes, we would like our model to be a bit more granular than the tables.

A classical example is an address class.  Let’s say we have the following client class:

public class Client
{
    public int ClientID { get; set; }
    [Required]
    [StringLength(32, MinimumLength=2)]
    public string ClientName { get; set; }
    public Address ResidentialAddress { get; set; }
    public Address DeliveryAddress { get; set; }
}

public class Address
{
    [Required]
    public int StreetNumber { get; set; }
    [Required]
    [StringLength(32, MinimumLength=2)]
    public string StreetName { get; set; }
}

We do not want the two address properties to map to an entry in an address table.  Instead we want EF 4.1 to map the entire class to one table, flattening the address properties.  How do we go about that?  Enters complex types.

As we’ve seen previously, there’s always an attribute and a model-builder path when we want to override conventions.  As I mentioned, I recommend going with the attributes when we enrich the model with business elements (e.g. required field).  I consider complex types as a detail of mapping classes to tables, hence I won’t use attributes.  I’ll do it all the model builder way:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
    base.OnModelCreating(modelBuilder);

    modelBuilder.Entity<Client>().Property(x => x.ClientID)
        .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    modelBuilder.ComplexType<Address>();
    modelBuilder.Entity<Client>().Property(i => i.ResidentialAddress.StreetNumber).HasColumnName("ResStreetNumber");
    modelBuilder.Entity<Client>().Property(i => i.ResidentialAddress.StreetName).HasColumnName("ResStreetName");
    modelBuilder.Entity<Client>().Property(i => i.DeliveryAddress.StreetNumber).HasColumnName("DelStreetNumber");
    modelBuilder.Entity<Client>().Property(i => i.DeliveryAddress.StreetName).HasColumnName("DelStreetName");

}

First I specify that client-id is an identity column (auto-incremental).  Then I specify that the address class is a complex type.  You can also put [ComplexType] on top of the class if you want.  Then I do map each sub property (handing out the path of the sub property in the lambda expression) to a column name.  This generates the following table:

image

Now I can use the models:

    using (var context1 = new MyDomainContext())
    {
        var client = new Client
        {
            ClientName = "Joe",
            ResidentialAddress = new Address
            {
                StreetNumber = 15,
                StreetName = "Oxford"
            },
            DeliveryAddress = new Address
            {
                StreetNumber = 514,
                StreetName = "Nolif"
            }
        };
        context1.Clients.Add(client);

        context1.SaveChanges();
    }
    using (var context2 = new MyDomainContext())
    {
        var clients = from w in context2.Clients
                      where w.ClientName == "Joe"
                      select w;

        foreach (var client in clients)
        {
            Console.WriteLine("client residential StreetNumber:  " + client.ResidentialAddress.StreetNumber);
            Console.WriteLine("client residential StreetName:  " + client.ResidentialAddress.StreetName);
            Console.WriteLine("client delivery StreetNumber:  " + client.DeliveryAddress.StreetNumber);
            Console.WriteLine("client delivery StreetName:  " + client.DeliveryAddress.StreetName);
        }
    }

The big caveat with complex types is the null management.  Even if all the properties in the complex type are nullable, you can’t just hand out a null complex type.  For instance, in this case, even if street name & street number wouldn’t be required, I couldn’t have a client with a null residential address:  I would need to new an address with everything null in it.  The same way, when you retrieve an entity, EF 4.1 will always new the complex type even if everything is null in it.


5 responses

  1. virusswb 2011-05-13 at 00:50

    sorry, but when i want to set complex type property to null, what can i do? thank you !!!

  2. Vincent-Philippe Lauzon 2011-05-13 at 07:22

    No can do man, it’s a limitation of EF 4.1. Your complex type property must be non-null. All properties of the complex type might be null though, but there is no convention where a null complex type property means all-null inside-properties.

    Hopefully, this will come in future versions!

  3. virusswb 2011-05-15 at 09:32

    thank you for your reply, hope this will come in future version.

  4. George R 2011-11-28 at 18:28

    And for future readers, to do this with code-first fluent configuration, inherit your config from ComplexTypeConfiguration rather than EntityTypeConfiguration

  5. Vincent-Philippe Lauzon 2011-11-29 at 09:18

    Hi George,

    Can you give a little more detail on that? Is that related to the ability of having a null complex type property or…?

Leave a comment