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
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.
I’m glad that you like it.
//Daniel
Pingback: DotNetShoutout