Taking something that can't be done, and then doing it.

by Jiří {x2} Činčura

You are reading

Custom conventions in Entity Framework 6 helping Firebird - part 2

Few days ago I wrote a post “Custom conventions in Entity Framework 6 helping Firebird”. Arthur Vickers from Entity Framework team had a good question whether it works also for columns and tables that are generated by Entity Framework (like join tables for M:N, FK columns (if not in model), etc.). And it actually does not. :) For this you have to dig a little bit deeper and use model-based convention. For this type of convention you have to write a class as there’s (currently, alpha 3) no way to do it using fluent API in a lightweight way (and I would not expect this to change, this isn’t common scenario).

Anyway for properties to work you have to implement IDbConvention<EdmProperty> and for table IDbConvention<EntityType> (not EntitySet, you need to be able to see whether somebody set the table name already or not which for me is easier from EntityType type). Don’t ask me how I found this. A lot of trial and error. And actually a lot of memories from around 2010 and Entity Framework v1 (link, link (anybody here ever explored MetadataProperties?)) :).

class FirebirdFriendlyModelConvention : IDbConvention<EdmProperty>, IDbConvention<EntityType>
{
    public void Apply(EdmProperty dbDataModelItem, EdmModel model)
    {
        var preferredName = (string)dbDataModelItem.Annotations.First(x => x.Name == "PreferredName").Value;
        if (preferredName == dbDataModelItem.Name)
            dbDataModelItem.Name = FirebirdNamingConvention.CreateName(dbDataModelItem.Name);
    }

    EntityType GetRootType(EntityType entityType)
    {
        if (entityType.BaseType != null)
            return GetRootType((EntityType)entityType.BaseType);
        return entityType;
    }

    string GetTableName(ICollection<DataModelAnnotation> anotations)
    {
        var anotation = anotations.FirstOrDefault(x => x.Name == "TableName");
        if (anotation == null)
            return null;
        return (string)anotation.Value;
    }

    public void Apply(EntityType dbDataModelItem, EdmModel model)
    {
        var entitySet = model.Containers.First().EntitySets.SingleOrDefault(e => e.ElementType == GetRootType(dbDataModelItem));
        var tableName = GetTableName(dbDataModelItem.Annotations);
        if (tableName == null)
        {
            tableName = FirebirdNamingConvention.CreateName(dbDataModelItem.Name);
            entitySet.GetType().GetProperty("Table").SetValue(entitySet, tableName);
        }
    }
}

As you can see it’s not magic, if you know where to look at. Because the Table property is currently (alpha 3) not public I was forced to use reflection. There’s the related work item.

With this conventions, hopefully, all the tables and columns, except specified explicitly, will be renamed to “common” Firebird naming.

Comments for this entry

 
comments powered by Disqus