Entity framework 4 – CTP3 – Complex type mapping

I got a question on my last post of how complex type mapping should be dealt with so I fired up Visual Studio and put together something.

What you need to do as a consumer of the lib

Lets say my Customer has a property Address and I want to store it in the Customer-table, or more correctly, “together with the Customer entity” as an embedded object.

Step 1 – Create the Address class

[Serializable]
public class Address
{
    public string Street { get; set; }
    public int? Zip { get; set; }
}

Step 2 – Add it to the Customer

[Serializable]
public class Customer
    : IEntity
{
    public int Id { get; set; }

    public byte[] Version { get; set; }

    public string Firstname { get; set; }

    public string Lastname { get; set; }

    public EfAddress Address { get; set; }

    public string Name
    {
        get { return string.Format("{0} {1}", Firstname, Lastname).Trim(); }
    }

    public Customer()
    {
        Address = new Address();
    }
}

Note! You must set-up an instance of the Address, otherwise you will get an exception, complaining about an null-address.

Step 3 – Map the Complex type

[Serializable]
public class AddressMapping 
    : ComplexTypeConfiguration<Address>
{
    public AddressMapping()
    {
        Property(o => o.Street)
            .HasMaxLength(50)
            .IsUnicode()
            .IsOptional();
        Property(o => o.Zip);
    }
}

Instead of extending EntityConfiguration<T> you extend ComplexTypeConfiguration<T>, then just provide the mappings just as you would do with an entity.

That’s it! You are done. The generated table will now contain fields like: Address_Street; Address_Zip. If you like to control the namings of the fields: use MapSingeType inside of the AddressMapping-class.

The changes to get it to work

To get it to work I had to add one method to the EfObjectContextBuilder, namely: RegisterComplexType as well as tweak RegisterMappings.

/// <summary>
/// Registers a complex-type.
/// </summary>
/// <typeparam name="TComplex">The complext-type being mapped.</typeparam>
/// <param name="mapping">The mapping-configuration.</param>
public void RegisterComplexType<TComplex>(ComplexTypeConfiguration<TComplex> mapping)
{
    Configurations.Add(mapping);
}

The Assembly resolvers (EfMappingsResolverByAssemblyScan, EfMappingsResolverByInterfaceFilterType) will locate the AddressMapping-type and I have updated EfObjectContextBuilder public void RegisterMappings(IEfMappingsResolver mappingsResolver) so that it will handle both EntityConfiguration<T> as well as CompexTypeConfiguration<T>

/// <summary>
/// Registers a pluraized Entity-Set as well as the mappings for an Entity by
/// traversing the Types of <see cref="EntityConfiguration{TEntity}"/> returned
/// by the Resolver.
/// </summary>
/// <param name="mappingsResolver">The mappings resolver.</param>
public void RegisterMappings(IEfMappingsResolver mappingsResolver)
{
    var mappingTypes = mappingsResolver.Resolve().ToList();

    if(mappingTypes.Count < 1)
        throw  new DataException(string.Format(StorageExceptions.EfObjectContextBuilder_RegisterMappings, typeof(IEfMappingsResolver).Name));

    var registerMapping = GetType().GetMethod("RegisterMapping");
    var registerComplexType = GetType().GetMethod("RegisterComplexType");

    foreach (var mappingType in mappingTypes)
    {
        var configurationType = mappingType.BaseType;

        var registrationMethod = (configurationType.Name.StartsWith("ComplexTypeConfiguration"))
                                        ? registerComplexType
                                        : registerMapping;

        var typeBeingConfigured = configurationType.GetGenericArguments()[0];
        var generic = registrationMethod.MakeGenericMethod(typeBeingConfigured);
        generic.Invoke(this, new[] { Activator.CreateInstance(mappingType) });
    }
}

If you are consuming the internals of the ContextBuilder you can use ComplexType<T>() which will return a ComplexTypeConfiguration instance that’s allready hooked up to the ContextBuilder.

The code can be downloaded here. Note!, I have provided the complete solution of my Core-lib. The code you are interested in is located in the project: Pls.Core.Storage under the namespace Ef. If you look at the IntegrationTestsOf-project, you will find EfCustomer and EfAddress etc. The naming is due to the fact that Entity framework can’t handle two classes having the same name within one assembly. Since I have a Linq to SQL implementation in there as well, I had to name them “L2SqlCustomer” and “EfCustomer“.

//Daniel

About these ads

3 thoughts on “Entity framework 4 – CTP3 – Complex type mapping

  1. I like the approach. With the previous (CTP2) version of your code I had gone down the route of adding a RegisterComplexTypes public method, and using another IComplexTypeMappingFilter for picking up the mappings by reflection.

    The way you have shown here fits into the new MappingResolver construct very nicely.

  2. Pingback: DotNetShoutout

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s