Как программно читать метаданные EF DbContext?
У меня есть приложение, которое использует EF-CodeFirst 5 (dll ver 4.4.0.0, на.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?
РЕДАКТИРОВАТЬ: Используя ваш список навигационных свойств в качестве отправной точки, я получил все, что мне нужно, как это:
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 - не моя вежливость, кредиты отправляются Джону Скиту, код создан с помощью этого ответа.
Вот мой метод 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});
}