Monthly Archives: March 2013

My First Encounter with Custom Conventions in Entity Framework 6

This post was written before EF6 RTM was released and is out-of-date. For up-to-date information see the article on model based conventions on msdn.

I was not really involved in the work related to custom conventions in EF6 and had not had a chance to try this feature so when I saw this question on stackoverflow I thought it would be a good opportunity to try it out and learn something new. The question was – how do I configure foreign keys using custom conventions? Entity Framework has a built-in convention which configures a property whose name is equal to a corresponding navigation property name suffixed with “Id” as a foreign key property. However if the model uses a different naming convention the built-in convention will not work. In the question from stackoverflow the convention for foreign key properties was “Id” + {NavigationPropertyName}. Since it was used consistently across the model it really did make sense to use custom conventions instead of configuring each foreign key property manually (i.e. using ForeignKey attribute or .HasForeignKey() method). So, I looked at the custom conventions specification and started reading about lightweight conventions. It looked simple – one line of code should be enough. Unfortunately I could not find a good way to configure foreign keys with this approach – .Properties() allows only configuring primitive properties and foreign keys need to be configured on navigation properties. .Entities() is great for configuring entity level settings but does not really allow to configure a property if you don’t know its name (and it did not seem possible to configure navigation properties this way either). Therefore I started looking at the Configuration-based Conventions. They are no longer a single line of code as the lightweight conventions and require creating a class implementing the IConfigurationConvention interface containing the logic for the convention. In return they allow for much more flexibility. The IConfigurationConvention interface is generic and requires two types – TMemberInfo and TConfiguration. TMemberInfo can be either Type or PropertyInfo and defines whether the IConfigurationConvention.Apply() method should be invoked for types or properties. The second parameter (TConfiguration) describes what we would like to configure and can be for instance a PropertyConfiguration, StructuralTypeConfiguration, PrimitivePropertyConfiguration etc. (the full list can be found in the feature specification). One of the configurations on the list is NavigationPropertyConfiguration. This seemed to be exactly what I needed to be able to configure foreign keys. Now the plan was simple – create a convention that for each navigation property will try finding a property named “Id” + {NavigationPropertyName} and if such a property exists configure it as a foreign key property. This is the convention I came up with:

public class NavigationPropertyConfigurationConvention
    : IConfigurationConvention<PropertyInfo, NavigationPropertyConfiguration>
{
    public void Apply(
                 PropertyInfo propertyInfo, 
                 Func<NavigationPropertyConfiguration> configuration)
    {
        var foreignKeyProperty = 
            propertyInfo.DeclaringType.GetProperty("Id" + propertyInfo.Name);

        if (foreignKeyProperty != null && configuration().Constraint == null)
        {
            var fkConstraint = new ForeignKeyConstraintConfiguration();
            fkConstraint.AddColumn(foreignKeyProperty);

            configuration().Constraint = fkConstraint;
        }           
    }
}

While not a single liner the convention is short and easy to follow. I created a very simple model to test it out:

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

    public ICollection<RelatedEntity> RelatedEntities { get; set; }
}

public class RelatedEntity
{
    public int Id { get; set; }

    public int IdPrincipal { get; set; }

    public Entity Principal { get; set; }
}

public class MyContext : DbContext
{
    public DbSet<Entity> Entities { get; set; }
    public DbSet<RelatedEntity> RelatedEntities { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Conventions.Add<NavigationPropertyConfigurationConvention>();
    }
}

Note that the convention had to be registered in the OnModelCreating() method.
To see that the convention was applied I set a break point in the convention an started my test app. Also to verify it worked I just dumped the edmx using edmx writer and checked my AssociationType (notice the PropertyRef Name under the Dependent element):

<Association Name="RelatedEntity_Principal">
  <End Role="Entity" Type="Self.Entity" Multiplicity="1">
    <OnDelete Action="Cascade" />
  </End>
  <End Role="RelatedEntity" Type="Self.RelatedEntity" Multiplicity="*" />
  <ReferentialConstraint>
    <Principal Role="Entity">
      <PropertyRef Name="Id" />
    </Principal>
    <Dependent Role="RelatedEntity">
      <PropertyRef Name="IdPrincipal" />
    </Dependent>
  </ReferentialConstraint>
</Association>

You may also want to check this interesting post on conventions from Rowan.

Advertisements

Hacking Linq to Entities with Expressions Part 1: Clean Generic Repository

The repository pattern is intended to create an abstraction layer between the data access layer and the business logic layer of an application and is often used with Entity Framework. To avoid creating repository classes specific to each entity type it is a common practice to create a generic repository class that can be used for any entity. However, most examples I have seen could not really be used easily for any entity. For instance the repository type requires to provide a generic type for the key (e.g. class Repository<TEntity, TKey>) which should not really be required as the type of the entity is provided. Another thing to look at it is the GetById() method. It’s interesting at least for a couple of reasons:

  • key properties of different entity types may have different types (e.g. string key properties vs. int key properties)
  • key properties of different entity types may have different names

I have seen several ways of solving the above problems, for instance: enforcing all entities to be derived from a base (possibly generic) entity type containing the key property (it does not solve the problem of entity types with different key property names since the names of all key properties in the whole model will be the same) or passing a lambda expression/query to the GetById() method (feels wrong to me since the GetById() method should just take the value of the key for which to return the entity and not query, property name and whatnot). I thought a little bit on this I concluded that it should be possible to create a generic repository type without any additional overhead since we already have all the information that is needed. We know the entity type – it is the generic parameter to the repository type. We are able to reason about the entity type (i.e. figure out what the key property are) because we do have the context and – as a result – we can access all the metadata. Finally – for the GetById() we have the value of the key since it is provided by the user. The only obstacle here is to create the right query to send to the database but this can be easily solved by creating the query dynamically with Expression Trees.
** EDIT **
As pointed out by Florim below in the comments there is a better option than building a dynamic query – namely DbSet.Find() method. Not it is simpler (it does not require building the dynamic query) but also it may save a trip to the database if the entity is available locally. I am leaving the rest of the post as is to justify the “Hacking Linq to Entities with Expressions” title.
** EDIT END **
Let’s start from finding the key property – given the entity type TEntity (the generic entity type of the repository) and a valid DbContext (derived) instance (passed as a parameter to the constructor of the repository type) we can find the key property as follows:

private PropertyInfo GetKeyProperty(DbContext context)
{
    if (_keyProperty == null)
    {
        var edmEntityType = 
            ((IObjectContextAdapter)context)
                .ObjectContext
                .MetadataWorkspace
                .GetItems<EntityType>(DataSpace.CSpace)
                .Single(e => e.Name == typeof(TEntity).Name);

        _keyProperty = 
            typeof(TEntity)
                .GetProperty(
                    edmEntityType.KeyMembers.Single().Name, 
                    BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

        if (_keyProperty == null)
        {
            throw new InvalidOperationException("Key property not found.");
        }
    }

    return _keyProperty;
}

Building the filter (i.e. the e => e.{keyProperty} == value) using Expression Trees is just a few lines of code:

private IQueryable<TEntity> Filter<TKey>(
    IQueryable<TEntity> dbSet,
    PropertyInfo keyProperty,
    TKey value)
{
    var entityParameter = Expression.Parameter(typeof(TEntity), "e");

    var lambda =
        Expression.Lambda<Func<TEntity, bool>>(
            Expression.Equal(
                Expression.Property(entityParameter, keyProperty),
                // no cast required if the passed value is of the 
                // same type as the key property
                typeof(TKey) == keyProperty.PropertyType ?
                    (Expression)Expression.Constant(value) :
                    (Expression)Expression.Convert(
                        Expression.Constant(value), keyProperty.PropertyType)),
                entityParameter);

    return dbSet.Where(lambda);
}

And finally we will connect the dots and create the GetById() method:

public TEntity GetById<TKey>(TKey value)
{
    return Filter(
        _context.Set<TEntity>(),
        GetKeyProperty(_context), value).SingleOrDefault();
}

Yes, the GetById() is generic. This is to avoid the value to be of the object type. Note that this does not add any overhead since the generic type does not have to be provided when invoking this method – the compiler is able to infer the type from the value of the parameter. In addition the Filter method will add a cast if the type of the passed value is different from the type of the key property (which will result in an exception at runtime if the provided value cannot be cast to the type of the key property).
For completeness here is the generic repository class (it does not include the Add, Delete etc. methods as they are not as interesting to me as the GetById() method):

public class Repository<TEntity> where TEntity : class
{
    private readonly DbContext _context;

    // for brevity composite keys are not supported
    private PropertyInfo _keyProperty;

    public Repository(DbContext context)
    {
        _context = context;
    }

    public TEntity GetById<TKey>(TKey value)
    {
        return Filter(
            _context.Set<TEntity>(),
            GetKeyProperty(_context), value).SingleOrDefault();
    }

    private IQueryable<TEntity> Filter<TKey>(
        IQueryable<TEntity> dbSet,
        PropertyInfo keyProperty,
        TKey value)
    {
        var entityParameter = Expression.Parameter(typeof(TEntity), "e");

        var lambda =
            Expression.Lambda<Func<TEntity, bool>>(
                Expression.Equal(
                    Expression.Property(entityParameter, keyProperty),
                    // no cast required if the passed value is of the
                    // same type as the key property
                    typeof(TKey) == keyProperty.PropertyType ?
                        (Expression)Expression.Constant(value) :
                        (Expression)Expression.Convert(
                            Expression.Constant(value), keyProperty.PropertyType)),
                    entityParameter);

        return dbSet.Where(lambda);
    }

    private PropertyInfo GetKeyProperty(DbContext context)
    {
        if (_keyProperty == null)
        {
            var edmEntityType =
                ((IObjectContextAdapter)context)
                    .ObjectContext
                    .MetadataWorkspace
                    .GetItems<EntityType>(DataSpace.CSpace)
                    .Single(e => e.Name == typeof(TEntity).Name);

            _keyProperty =
                typeof(TEntity)
                    .GetProperty(
                        edmEntityType.KeyMembers.Single().Name,
                        BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);

            if (_keyProperty == null)
            {
                throw new InvalidOperationException("Key property not found.");
            }
        }

        return _keyProperty;
    }

    // other "less interesting" methods
}

… and an example of how to use it. For the following, simple model with entities having keys with different names and of different types:

public class Customer
{
    public string CustomerId { get; set; }
    
    // ...
}

public class Order
{
    public Guid OrderId { get; set; }

    // ...
}

public class Item
{
    public int ItemId { get; set; }

    // ...
}

public class Context : DbContext
{
    public DbSet<Customer> Customers { get; set; }
    public DbSet<Order> Orders { get; set; }
    public DbSet<Item> Items { get; set; }
}

The entities can be retrieved by id as simple as:

using (var ctx = new Context())
{
    Console.WriteLine(
        new Repository<Customer>(ctx)
            .GetById("ALFKI").CustomerId);

    Console.WriteLine(
        new Repository<Order>(ctx)
            .GetById(new Guid("00000000-0000-0000-C000-000000000046")).OrderId);

    Console.WriteLine(
        new Repository<Item>(ctx)
            .GetById((byte)1).ItemId);
}

As you can see the code is clean – no extraneous information is provided – just the type of the entity and the value of the key (Yeah, the cast to byte is not needed – it is just to test that the logic in the dynamically built filter works)