Подтвердить что ты не робот

Сначала введите код платформы Entity Framework. Поиск первичного ключа

Как найти, какое свойство класса является первичным ключом кода Entity Framework? Первый объект POCO?

Обратите внимание, что сопоставление строк для имени Id/class + "Id" является плохой опцией. Должен быть какой-то способ выкопать соглашение, используемое Entity Framework, и надежно получить свойство ключа.

Спасибо заранее.

4b9b3361

Ответ 1

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

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 предназначен только для простых сценариев, где вы не беспокоитесь о таких деталях, как метаданные отображения.

Ответ 2

В случае, если это помогает кому-то, мне нужно было это сделать, не зная тип заранее (поэтому я не мог легко сделать 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, но это лучшее, что я мог найти.

Ответ 3

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

Пример:

public static IEnumerable<string> GetPrimaryKeyPropertyNames(DbContext db, Type entityType)
{
    return db.Db(entityType).Pks.Select(x => x.PropertyName);
}

Ответ 4

У меня возникла проблема с обоими вышеупомянутыми подходами из-за наследования типа Table Per Type. Моя рабочая версия (основанная на решении @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;
        }

Ответ 5

Как и пользователь 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);