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.