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

Entity framework 4 – How-to reuse mappings and add a Concurrency token

I’m working on a larger sample (I will write or do a screencast about this shortly) which uses a distributed solution with WCF and the Entity framework 4 Code-only approach. Doing this I wanted to add a Concurrency token so that EF would trap concurrency violations, e.g:

1. I fetch an object which becomes detached.
2. The same object (seen to business equality) gets fetched in some other part of the application.

We now have to different instances representing the same domain object.

3. The entity from step 2 is updated and persisted.
4. When the entity from step 1 is sent to the object context for update, EF uses the Concurrency token field and gets 0-rows affected.

How do I add an concurrency token?
1. Add a property in your entity, which is of datatype byte[].
2. Add mapping for the field, stating that the added field is the Concurrency token and of which type (timestamp or rowversion)

Since I want all my entities to have this I add the property to my Entity-baseclass. I also have defined a base mapping class that maps this and the Id property for my entities, so that I don’t have to specify this all the time.

The Entities

public abstract class Entity
{
    public virtual int Id { get; set; }

    public virtual byte[] ConcurrencyToken { get; set; }
}
[Serializable]
public class UserAccount
    : Entity
{
    public virtual string Username { get; set; }

    public virtual string Password { get; set; }

    public virtual string Email { get; set; }

    public virtual string Firstname { get; set; }

    public virtual string Lastname { get; set; }
}

The mappings

[Serializable]
public abstract class EntityMapping<TEntity> : EntityConfiguration<TEntity>
    where TEntity : Entity
{
    protected EntityMapping()
    {
        HasKey(u => u.Id);
        Property(u => u.Id).IsIdentity();

        Property(u => u.ConcurrencyToken)
            .IsRequired()
            .IsConcurrencyToken()
            .HasStoreType("timestamp")
            .StoreGeneratedPattern = StoreGeneratedPattern.Computed;
    }
}
[Serializable]
public class UserAccountMapping : EntityMapping<UserAccount>
{
    public UserAccountMapping()
    {
        Property(u => u.Email)
            .IsRequired()
            .IsNotUnicode()
            .MaxLength = 150;

        Property(u => u.Username)
            .IsRequired()
            .IsUnicode()
            .MaxLength = 20;

        Property(u => u.Password)
            .IsRequired()
            .IsUnicode()
            .MaxLength = 20;

        Property(u => u.Firstname)
            .IsRequired()
            .IsUnicode()
            .MaxLength = 100;

        Property(u => u.Lastname)
            .IsRequired()
            .IsUnicode()
            .MaxLength = 100;
    }
}

//Daniel

Entity framework 4 – Code only – How-to change target names in the database table

For a couple of days ago I got the question of “my mappings doesn’t work”. I looked at it and found that the database tables were generated upfront and not by the ObjectContext. The columnnames had other names. Than how-do you solve this missmatch if you really do want to have separate namings? Using MapSingleType.

Personally, I let the context create the database and the tables, since I want the model to drive the database design.

The entity

[Serializable]
public class Car
{
    public string LicencePlate { get; set; }
    public int CountryCode { get; set; }
}

The mapping

[Serializable]
public class CarMapping : EntityConfiguration<Car>
{
    public CarMapping()
    {
        HasKey(c => new { c.CountryCode, c.LicencePlate });
        MapSingleType(c => 
            new
                {
                    TheCountryCode = c.CountryCode, 
                    TheLicencePlate = c.LicencePlate
                }).ToTable("dbo.TheCars");
    }
}

The result
Table: TheCars (you don’t have to use “ToTable”).
Columns:

  • TheCountryCode
  • TheLicencePlate

//Daniel

Entity framework 4 – Code-only, How-to map a combined PK

I got a question today of how to map an entity with a primarykey that is built up using two fields. The answer: using an anonymous type.

The entity

[Serializable]
public class Car
{
    public string LicencePlate { get; set; }
    public int CountryCode { get; set; }
}

The mapping

[Serializable]
public class CarMapping : EntityConfiguration<Car>
{
    public CarMapping()
    {
        HasKey(c => new {c.CountryCode, c.LicencePlate});
    }
}

//Daniel