I can see from the following example how to get the table name of an OSpace type:
https://lowrymedia.com/2014/06/10/ef6-1-mapping-between-types-tables-including-derived-types/
But how do I go about getting the SSpace column name from an OSpace property name (i.e. CLR type property)?
By browsing the MetadataProperties from the corresponding CSpace property, I can see there is a "Configuration" entry containing the column name if changed using the Fluid API or ColumnAttribute, but the value of the entry is an internal class on EF's part. Is it at all possible?
I have browsed a few answers regarding this topic, but none of them take into account the Fluid API configuration.
P.S. the specific property I'm looking for is scalar, if that can simplify things...
To get the column name, you have to first get the EdmProperty
associated with that column in the “structural space” (SSpace
). I provide code to do that below. Once you have the EdmProperty
, the name of the column is simply EdmProperty.Name
:
string GetColumnName(DbContext context, PropertyInfo property) {
return GetStructuralSpaceEdmProperty(context, property).Name;
}
This is based on an article. That article gives you enough information to map all the way to the structural EntityType
. I added a bit at the end to do the actual property mapping to get the EdmProperty
representing the column. As the article states, these APIs require ≥EntityFramework-6.1.
EdmProperty GetStructuralSpaceEdmProperty(DbContext context, PropertyInfo property) {
IObjectContextAdapter adapter = context;
var metadata = adapter.ObjectContext.MetadataWorkspace;
// First, you map the Object Space to the Conceptual Space.
var objectItemCollection = (ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace);
var objectEntityType = metadata.GetItems<EntityType>(DataSpace.OSpace)
.Single(oet => objectItemCollection.GetClrType(oet) == property.DeclaringType);
// Note: we are assuming that CSpace and OSpace name their properties the
// same instead of trying to use EF’s own OSSpace mappings here.
var conceptualEntityType = metadata.GetItems<EntityType>(DataSpace.CSpace)
.Single(cet => objectEntityType.Name == cet.Name);
var conceptualEdmProperty = conceptualEntityType.Properties
.Single(ep => ep.Name == property.Name);
// Then you map the conceptual space onto the structural space.
var entitySet = metadata.GetItems<EntityContainer>(DataSpace.CSpace)
.Single().EntitySets
.Single(es => es.ElementType.Name == conceptualEntityType.Name);
var entityMapping = metadata.GetItems<EntityContainerMapping>(DataSpace.CSSpace)
.Single().EntitySetMappings
.Single(esm => esm.EntitySet == entitySet);
// The entity may be split to different tables or fragments.
var fragments = entityMapping.EntityTypeMappings
.SelectMany(etm => etm.Fragments);
var propertyMappings = fragments.SelectMany(f => f.PropertyMappings);
// Normal properties will be “ScalarPropertyMapping”.
// Depending on what information you are seeking or your
// model, you may be interested in other PropertyMapping.
var structuralSpaceProperty = propertyMappings
.OfType<ScalarPropertyMapping>()
.Single(pm => pm.Property == conceptualEdmProperty).Column;
return structuralSpaceProperty;
}
Note that once you have EdmProperty
in structural space, there are a bunch of other useful properties you can read from it. For example, for SQL Server, EdmProperty.IsUnicode
will be true
for NVARCHAR
/NCHAR
and false
for VARCHAR
/CHAR
types whereas this property is not set to a useful value in the conceptual space.
Alex D. James’s blog post “Tip 10 — How to understand Entity Framework jargon” explains some of the terms of the API which do not make sense on their own. DataSpace.OSpace
stands for “Object Space”, meaning the .net POD classes. DataSpace.SSpace
stands for “Structural Space”, probably named after “structured” in the term “SQL” and thus meaning it most directly describes the backend database. DataSpace.CSpace
stands for “Conceptual Space” which seems intended to be a neutral space which both the “Object Space” and “Structural Space” can map into. DataSpace.OCSpace
stands for the mapping from the object space onto the conceptual space. We bypass this mapping because we assume that property names in the object space are the same as in our .net types. DataSpace.CSSpace
stands for the mapping of conceptual space onto structural space. We use this mapping because the model may be configured to use a different column name via the fluent API or ColumnAttribute
.
The metadata API of EF seems to assume that the consumer of the API has an understanding of the internals of EF to an extent. It is not made in a very type safe way which helps consumers. For example, the fact that we had to use Enumerable.OfType<TResult>
to get to ScalarPropertyMapping
means that one has to know to expect the collection to have ScalarPropertyMapping
instances in it. Likewise, the MetadataWorkspace.GetItems<T>()
method requires us to know that the sorts of items one would find in the metadata include EntityType
. Thus, a deep understanding of the internals of EF or complete examples are necessary to write code that consumes the mapping portion of these APIs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With