Код Entity Framework первым. Найти первичный ключ - PullRequest
32 голосов
/ 31 августа 2011

Как узнать, какое свойство класса является первичным ключом POCO сущности Code Entity Framework?Должен быть какой-то способ найти соглашение, используемое Entity Framework и надежно получить свойство ключа.

Заранее спасибо.

Ответы [ 5 ]

55 голосов
/ 31 августа 2011

Вы можете попросить метаданные сопоставления получить имена ключевых свойств (их может быть больше одного):

ObjectContext objectContext = ((IObjectContextAdapter)dbContext).ObjectContext;
ObjectSet<YourEntity> set = objectContext.CreateObjectSet<YourEntity>();
IEnumerable<string> keyNames = set.EntitySet.ElementType
                                            .KeyMembers
                                            .Select(k => k.Name);

Получив имена ключей, вы можете использовать отражение для доступа к их значениям.

Как вы видите, подход возвращается к ObjectContext API, потому что DbContext API предназначен только для простых сценариев, в которых вам не нужны такие детали, как отображение метаданных.

25 голосов
/ 20 августа 2014

В случае, если это кому-нибудь поможет, мне нужно было делать это, не зная заранее типа (поэтому я не мог легко сделать CreateObjectSet<YourEntity>(), потому что я не знал YourEntity), поэтому я мог адаптировать решение @Ladislav к следующему:

// variable "type" is a System.Type passed in as a method parameter
ObjectContext objectContext = ((IObjectContextAdapter)this.context).ObjectContext;
IEnumerable<string> retval = (IEnumerable<string>)objectContext.MetadataWorkspace
    .GetType(type.Name, type.Namespace, System.Data.Entity.Core.Metadata.Edm.DataSpace.CSpace)
    .MetadataProperties
    .Where(mp => mp.Name == "KeyMembers")
    .First()
    .Value;

Кажется странным, что MetadataWorkspace.GetType требует строки имени типа и пространства имен вместо System.Type, но это лучшее, что я мог найти.

14 голосов
/ 14 июня 2016

В EF 6.1 есть метод расширения Db(), который делает это проще.

Пример:

public static IEnumerable<string> GetPrimaryKeyPropertyNames(DbContext db, Type entityType)
{
    return db.Db(entityType).Pks.Select(x => x.PropertyName);
}
9 голосов
/ 18 сентября 2014

У меня были проблемы с обоими вышеупомянутыми подходами из-за наследования таблицы на тип. Моя рабочая версия (основанная на решении @ S'pht'Kr, но с использованием DataSpace.OSpace, а не DataSpace.CSpace по этой причине) ниже:

        protected IEnumerable<string> GetKeyPropertyNames()
        {
            var objectContext = ((System.Data.Entity.Infrastructure.IObjectContextAdapter) this.Context).ObjectContext;

            return GetKeyPropertyNames(typeof (TEntity), objectContext.MetadataWorkspace);
        }

        private static IEnumerable<string> GetKeyPropertyNames(Type type, MetadataWorkspace workspace)
        {
            EdmType edmType;

            if (workspace.TryGetType(type.Name, type.Namespace, DataSpace.OSpace, out edmType))
            {
                return edmType.MetadataProperties.Where(mp => mp.Name == "KeyMembers")
                    .SelectMany(mp => mp.Value as ReadOnlyMetadataCollection<EdmMember>)
                    .OfType<EdmProperty>().Select(edmProperty => edmProperty.Name);
            }

            return null;
        }
3 голосов
/ 27 июня 2016

Как и пользователь rashleighp, я также хотел вариант ответа пользователя Ladislav Mrnka, который должен знать только тип во время выполнения, а не знать тип во время компиляции.Также как и пользователь rashleighp, решение пользователя S'pht'Kr у меня не сработало, но его решение сработало.Ниже я внесу свой вклад в этот разговор, предоставив более простую версию его ответа, которая сработала для меня.Однако я только что узнал о решении от пользователя anjdreas, и именно этим я буду пользоваться.

// variable "type" is a System.Type passed in as a method parameter
((IObjectContextAdapter)context)
    .ObjectContext
    .MetadataWorkspace
    .GetItem<EntityType>(type.FullName, DataSpace.OSpace)
    .KeyProperties
    .Select(p => p.Name);
...