tabs ↹ over ␣ ␣ ␣ spaces

by Jiří {x2} Činčura

TPH mapping discriminator condition from MetadataWorkspace

25 Aug 2010 2 mins Entity Framework

The MetadataWorkspace contains a lot of useful information. Recently I was facing a challenge to get information about TPH (table per hierarchy) inheritance conditions for particular type. Sure, it’s in EDMX file and/or in MSL file. So you can parse the XML and get the info. I was on the other hand more interested getting the info from MetadataWorkspace, partially as a good “brain training” 8) side project.

Sadly the information about the mapping is very limited. Most interesting parts are not public, thus you’re forced to use reflection. So it’s a lot back and forth with, in my case, QuickWatch window. It helps a little to be familiar with MSL file structure.

static object GetNonPublicPropertyValue(this object o, string propertyName)
{
	return o.GetType()
		.GetProperty(propertyName, BindingFlags.NonPublic | BindingFlags.Instance)
		.GetValue(o, null);
}
public static IEnumerable<KeyValuePair<string, object>> GetMappingConditions<T>(this ObjectContext context)
	where T : class
{
	string typeToSearch = typeof(T).Name;
	var mapping = context.MetadataWorkspace.GetItemCollection(DataSpace.CSSpace).First();
	return ((IEnumerable<object>)mapping.GetNonPublicPropertyValue("EntitySetMaps"))
		.SelectMany(entitySetMap => (IEnumerable<object>)entitySetMap.GetNonPublicPropertyValue("TypeMappings"))
		.Where(typeMapping =>
			((IEnumerable<dynamic>)typeMapping.GetNonPublicPropertyValue("IsOfTypes")).Any(type => type.Name == typeToSearch)
			||
			((IEnumerable<dynamic>)typeMapping.GetNonPublicPropertyValue("Types")).Any(type => type.Name == typeToSearch))
		.SelectMany(typeMapping => (IEnumerable<object>)typeMapping.GetNonPublicPropertyValue("MappingFragments"))
		.SelectMany(mappingFragment => (IEnumerable<object>)mappingFragment.GetNonPublicPropertyValue("AllProperties"))
		.Where(mappingFragment => mappingFragment.GetType().Name == "StorageConditionPropertyMapping")
		.Select(condition =>
			{
				bool? isNull = (bool?)condition.GetNonPublicPropertyValue("IsNull");
				string value = (string)condition.GetNonPublicPropertyValue("Value");
				return new KeyValuePair<string, object>((string)((dynamic)condition.GetNonPublicPropertyValue("ColumnProperty")).Name, (isNull.HasValue ? (object)isNull.Value : (object)value));
			});
}

Because the code is heavily using reflection and non public members, it’s possible it’ll not work other/future versions of Entity Framework. I tested it with current version, version 4.

It’s written in a compact way. If you want to further dig into partial results, I recommend to split it into foreach loops and do small steps. That’s in fact how I started and was incrementally discovering the information available at given level.

As all the data are not public, I’ll not describe how and why it is as it is. I did it using trial and error process. 😃 Maybe there’s other/simpler path. Feel free to use comments if you find one.

Profile Picture Jiří Činčura is an independent developer, .NET, C# and Firebird expert, focusing on data and business layers, language constructs, parallelism, databases and performance. He's Microsoft Most Valuable Professional and frequent speaker. You can read his articles, guides and tips and tricks at www.tabsoverspaces.com.