Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mapping from IEdmEntity to CLR

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;
    }
like image 838
CharlesNRice Avatar asked Apr 10 '14 21:04

CharlesNRice


2 Answers

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.

like image 139
rob3c Avatar answered Oct 13 '22 16:10

rob3c


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);
like image 27
codeworx Avatar answered Oct 13 '22 14:10

codeworx