Reputation: 3259
I'm trying to find a way to go from an IEdmEntity to the CLR Type in entity framework. From the casting to ObjectContext to get Metadata. I'm using the DataSpace.OCSpace to get access to the mapping. I believe that is correct but I might have the wrong DataSpace, the DataSpaces are not clear in my head of which does what, even after this blog http://blogs.msdn.com/b/alexj/archive/2009/04/03/tip-10-understanding-entity-framework-jargon.aspx.
In the end I get back System.Data.Entity.Core.Mapping.MappingBase objects which doesn't do much for me. From the debugger it seems I could get access to what I want but those classes are marked internal and I can't cast to them.
Am I making this too hard or is there no way to go from an IEdmModel from Entity Framework back to the CLR Types it maps to?
Adding code to try and make it more clear what I'm working with and trying to get out
public Type GetIEdmEntityTypeToClrType(IEdmEntityTypeReference edmEntityType, DbContext context)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var fullname = edmEntityType.EntityDefinition().FullName();
EntityType entityType;
if (metadata.TryGetItem(fullname, DataSpace.CSSpace, out entityType))
{
//doesn't hit
}
if (metadata.TryGetItem(fullname, DataSpace.CSpace, out entityType))
{
//hits but can't get access to CLR Type that it's mapped too.
}
if (metadata.TryGetItem(fullname, DataSpace.OCSpace, out entityType))
{
//doesn't hit
}
if (metadata.TryGetItem(fullname, DataSpace.OSpace, out entityType))
{
//doesn't hit
}
if (metadata.TryGetItem(fullname, DataSpace.SSpace, out entityType))
{
//doesn't hit
}
return null;
}
Upvotes: 9
Views: 3822
Reputation: 2086
The *IEdm** interfaces you mentioned in both your question and answer are not used by Entity Framework per se (the EF6 NuGet package has no Microsoft.Data.Edm dependency), but are primarily used with OData service metadata (CSDL). Since entities declared in OData CSDL don't necessarily map to any particular CLR classes, you can only find their CLR types indirectly. (I think that confusion is why Andrew's EF-only answer assumed you had access to an EntityObject
.)
Fortunately, when presenting EF entities via OData, there's normally a 1:1 correspondence between the full names of the entities in the CSDL of both the OData service and EF model. Assuming that's the case, your can search using edmEntityType.FullName
as you did above, but you have to get the corresponding EF EntityType
from the ObjectContext
metadata first.
DataSpace.OCSpace
in MetadataWorkspace
was a reasonable place to look for the mapping, since that's where the Object Space <-> Conceptual Space mappings are stored. But as you discovered, while EF6's mapping API is supposedly public, ObjectTypeMapping
and its related classes are still marked internal
:(
However, it turns out that you don't need to do any ugly reflection hacks with the internal OCSpace
classes! You can get the mapped CLR type directly from your 'hit' in CSpace
like this:
var clrTypeMetadataPropName = @"http://schemas.microsoft.com/ado/2013/11/edm/customannotation:ClrType";
var clrType = (Type)
((IObjectContextAdapter)context).ObjectContext
.MetadataWorkspace
.GetItems<EntityType>(DataSpace.CSpace)
.Single(s => s.FullName == edmEntityType.FullName())
.MetadataProperties
.Single(p => p.Name == clrTypeMetadataPropName )
.Value;
Sure, it uses the 'internal' ClrType custom annotation key magic string, but everything is done through the current public API. I think that's as close as you can get to an 'official' solution until/unless the rest of the mapping API is made public.
Upvotes: 8
Reputation: 2745
This should work for entity and property types.
public static Type GetClrTypeFromCSpaceType(
this MetadataWorkspace workspace, EdmType cType)
{
var itemCollection = (ObjectItemCollection)workspace.GetItemCollection(DataSpace.OSpace);
if (cType is StructuralType) {
var osType = workspace.GetObjectSpaceType((StructuralType)cType);
return itemCollection.GetClrType(osType);
} else if (cType is EnumType) {
var osType = workspace.GetObjectSpaceType((EnumType)cType);
return itemCollection.GetClrType(osType);
} else if (cType is PrimitiveType) {
return ((PrimitiveType)cType).ClrEquivalentType;
} else if (cType is CollectionType) {
return workspace.GetClrTypeFromCSpaceType(((CollectionType)cType).TypeUsage.EdmType);
} else if (cType is RefType) {
return workspace.GetClrTypeFromCSpaceType(((RefType)cType).ElementType);
} else if (cType is EdmFunction) {
return workspace.GetClrTypeFromCSpaceType(((EdmFunction)cType).ReturnParameter.TypeUsage.EdmType);
}
return null;
}
usage
var entity = workspace.GetItems<EntityType>(DataSpace.CSpace).First();
var entityType = workspace.GetClrTypeFromCSpaceType(entity);
var propertyType = workspace.GetClrTypeFromCSpaceType(entity.Properties[0].TypeUsage.EdmType);
Upvotes: 4
Reputation: 3259
Here's what I have that works from my limited testing but really seems like a hack. Hoping someone else finds something better.
public Type ConvertIEdmEntityTypeToClr(IEdmEntityType edmEntityType, DbContext context)
{
var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;
var oSpace = metadata.GetItemCollection(DataSpace.OSpace);
var typeName = oSpace.GetItems<EntityType>().Select(e => e.FullName).FirstOrDefault(name =>
{
var fullname = name + ":" + edmEntityType.FullName();
MappingBase map;
return metadata.TryGetItem(fullname, DataSpace.OCSpace, out map);
});
return Type.GetType(typeName, false);
}
Assumes that the OSpace Identity is the same as the CLR name. Also assumes that ID for the OCSpace is the two put together separated by a :.
Upvotes: 1
Reputation: 3796
I assume you are using Entity Framework 6, where Mapping API is not public. Please have a look at new release of Entity Framework 6.1 RTM:
http://blogs.msdn.com/b/adonet/archive/2014/03/17/ef6-1-0-rtm-available.aspx
More specifically at the Public Mapping API feature:
https://entityframework.codeplex.com/wikipage?title=Public%20Mapping%20API
You should play with metadataWorkspace to get information about entity framework types and their mapping, for example all simple properties of your entity and their CLR types can be retrieved like this:
EntityObject entity = null; //your entity
MetadataWorkspace metadataWorkspace = dataContext.MetadataWorkspace;
Type currentEntityType = entity.GetType();
EntityType entityType = metadataWorkspace.GetItem<EntityType>(currentEntityType.FullName, DataSpace.OSpace);
var simpleProperties = entityType.Properties.Where(p => p.DeclaringType == entityType && p.TypeUsage.EdmType is SimpleType);
foreach (EdmProperty simpleProperty in simpleProperties)
{
Console.WriteLine(string.Format("Name: {0} Type: {1}", simpleProperty.Name,simpleProperty.TypeUsage));
}
Upvotes: 1