Как я могу программно программировать метаданные EF DbContext?

У меня есть приложение, которое использует EF-CodeFirst 5 (dll ver 4.4.0.0, on.net 4.0).

Мне нужно иметь возможность читать метаданные объекта, чтобы я мог, для данного типа записи получить следующую информацию:

  • какие свойства являются отношениями "один-много" (ссылочные сущности)
  • какие свойства являются отношениями много-одного (коллекции объектов, ссылающихся на текущий)
  • также приятно, но не совсем необходимо: какие свойства - много-много отношений (коллекции отношений)

Я могу получить эту информацию, написав петли foreach в списках свойств и затем "распознавая" их, полагаясь на все ссылки, являющиеся виртуальными, но я считаю, что это не "правильный" способ. Я знаю, что EdmxWriter может предоставить эту информацию в формате xml, но делает это, обратившись к InternalContext, который не является общедоступным, и я хочу получить строго типизированные списки/массивы напрямую, без использования этого xml. Какой API я должен использовать (если есть один для этого, кажется, что я не могу его найти)?

Ответы

Ответ 1

Горан, это должно заставить вас начать...
(Я не много играл с ним - в отладчике нужно немного экспериментировать, чтобы узнать, какие свойства/информация и как его получить)

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();
}

и более конкретно...

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;
    }
}

Некоторые из этих свойств, похоже, не подвергаются "широкой публике", поэтому вам нужно использовать отражение - или найти какой-то более умный способ, - но там много информации.



И еще информация об этих ссылках...

Как получить первое имя EntityKey для объекта в EF4

Как извлечь таблицу базы данных и имя столбца для свойства в объекте EF4?


ИЗМЕНИТЬ: Используя ваш список navigationProperties в качестве отправной точки, я получил все, что мне было нужно:

        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 - это не моя любезность, кредиты идут на Jon Skeet, код был создан с помощью этого ответа

Вот мой метод CreateLambdaExpression:

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});
}