How I can read EF DbContext metadata programmatically?

10,965

Gorane, this should get you started...
(I haven't played much with it - it takes a bit of experimenting in the debugger to see which properties / info and how to get it)

using (var db = new MyDbContext())
{
    var objectContext = ((IObjectContextAdapter)db).ObjectContext;
    var container = objectContext.MetadataWorkspace.GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace);
    foreach (var set in container.BaseEntitySets)
    {
        // set.ElementType.
        foreach (var metaproperty in set.MetadataProperties)
        {
            // metaproperty.
        }
    }

    // ...or... 

    var keyName = objectContext
        .MetadataWorkspace
        .GetEntityContainer(objectContext.DefaultContainerName, DataSpace.CSpace)
        .BaseEntitySets
        .First(meta => meta.ElementType.Name == "Question")
        .ElementType
        .KeyMembers
        .Select(k => k.Name)
        .FirstOrDefault();
}

and more specifically...

foreach (var set in container.BaseEntitySets)
{
    var dependents = ((EntitySet)(set)).ForeignKeyDependents;
    var principals = ((EntitySet)(set)).ForeignKeyPrincipals;
    var navigationProperties = ((EntityType)(set.ElementType)).NavigationProperties;
    foreach (var nav in navigationProperties)
    {
        // nav.RelationshipType;
    }
}

Some of these properties seem to not be exposed to 'general public' so you'd need to use reflection - or find some smarter way - but a good deal of info is in there.



And some more info in these links...

How to get first EntityKey Name for an Entity in EF4

How can I extract the database table and column name for a property on an EF4 entity?


EDIT: Using your list of navigationProperties as starting point, I got everything I needed like this:

        ManyToManyReferences = navigationProperties.Where(np =>
            np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
            np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
            .ToList();

        OneToManyReferences = navigationProperties.Where(np =>
            (np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One ||
            np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne) &&
            np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many)
            .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
            .ToList();

        ManyToOneReferences = navigationProperties.Where(np =>
            np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.Many &&
            (np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One ||
            np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.ZeroOrOne))
            .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
            .ToList();

        OneToOneReferences = navigationProperties.Where(np =>
            np.FromEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One &&
            np.ToEndMember.RelationshipMultiplicity == RelationshipMultiplicity.One)
            .Select(np => Extensions.CreateLambdaExpression<TEntity>(np.Name))
            .ToList();

CreateLambdaExpression method is not my courtesy, credits go to Jon Skeet, code was created with help of this answer

Here is my CreateLambdaExpression method:

public static Expression<Func<TEntity, object>> CreateLambdaExpression<TEntity>(string propertyName)
{
    ParameterExpression parameter = Expression.Parameter(typeof (TEntity), typeof (TEntity).Name);
    Expression property = Expression.Property(parameter, propertyName);

    return Expression.Lambda<Func<TEntity, object>>(property, new[] {parameter});
}
Share:
10,965

Related videos on Youtube

Goran Obradovic
Author by

Goran Obradovic

Responsible coder. #SOreadytohelp Blogger, husband and father at home. Freelancing software consultant based in Nuremberg. Amateur shutterbug elsewhere :) ###More about me: CV | LinkedIn | Twitter

Updated on September 15, 2022

Comments

  • Goran Obradovic
    Goran Obradovic over 1 year

    I have application which uses EF-CodeFirst 5 (dll ver 4.4.0.0, on .net 4.0).

    I need to be able to read entity metadata, so that I can, for a given entry type get following information:

    • which properties are one-many relations (referenced entities)
    • which properties are many-one relations (collections of entities referencing current one)
    • also nice but not absolutely necessary: which properties are many-many relations (collections of relations)

    I can get this info by writing foreach loops on lists of properties and then "recognizing" them by relying on all of the references being virtual, but I feel that is not "proper" way. I know that EdmxWriter can provide that information in xml format, but it does so by accessing InternalContext which is not publicly accessible and I want to get strongly typed lists/arrays directly, without using that xml. Which API should I use (if there is one for this, it seems that I cannot find it)?

  • Goran Obradovic
    Goran Obradovic about 11 years
    Thanks, this got me started, and I got what I need (without reflexion), expanded your answer to show how.
  • NSGaga-mostly-inactive
    NSGaga-mostly-inactive about 11 years
    that's great, thank you too - I'm going to keep this in my links - I had a few uses for that before - but never did 'elaborate' on that in too many details, this is very helpful piece of code. And a good question.
  • Kirk Woll
    Kirk Woll over 9 years
    It seems that ForeignKeyDependents and ForeignKeyPrincipals is marked internal in EntitySet for EF6.
  • InteXX
    InteXX about 9 years
    @GoranObradovic: What purpose does CreateLambdaExpression serve here? If you just want filtered lists of NavigationProperties, is it needed at all? If I could see the expression tree you ended up with I'd be able to understand it better. Could you edit the answer again to include the final CreateLambdaExpression code you ended up with? It's still unclear to me. Thanks.
  • Goran Obradovic
    Goran Obradovic about 9 years
    @InteXX answer already had link to another answer that gave me that method, but I included my simplified version I ended up using, it is just selector for property with given name. Anyway, try to use newer EF versions, this one sucks :)
  • InteXX
    InteXX about 9 years
    @GoranObradovic: I would absolutely LOVE to do this with my current EF6.1 DbContext. I thought this was the way to work Metadata with 6.1. Apparently not? I'm not finding documentation.
  • InteXX
    InteXX about 9 years
    @GoranObradovic: Got it, thanks. In fact I've been using some of that. Here's what I'm trying to do.
  • Rudey
    Rudey about 5 years
    Instead of finding the primary key, or navigation properties, is there a way to figure out which properties have (unique) indexes on them?