tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

Custom conventions in Entity Framework 6 helping Firebird – part 2

11 Mar 2013 2 mins Entity Framework, Firebird

Follow-up post.

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.

Profile Picture Jiří Činčura is .NET, C# and Firebird expert. He focuses on data and business layers, language constructs, parallelism, databases and performance. For almost two decades he contributes to open-source, i.e. FirebirdClient. He works as a senior software engineer for Microsoft. Frequent speaker and blogger at www.tabsoverspaces.com.