Entity Framework 4: Как найти первичный ключ? - PullRequest
12 голосов
/ 02 июня 2010

Я пытаюсь создать универсальный метод, используя EF4, чтобы найти первичный ключ объекта.

пример

public string GetPrimaryKey<T>()
{
    ...
}

Чтобы дать больше информации, я работаю над Tekpub StarterKit, и ниже приведен класс, который я пытаюсь запустить и запустить

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data.Objects;
using System.Data.Objects.ELinq;
using System.Data.Linq;
using Web.Infrastructure.Storage.EF4;

namespace Web.Infrastructure.Storage {
public class EFSession:ISession {
    PuzzleEntities _db;//This is an ObjectContext
    public EFSession() {
        _db = new PuzzleEntities();
    }

    public void CommitChanges() {
        _db.SaveChanges();
    }
    /// <summary>
    /// Gets the table provided by the type T and returns for querying
    /// </summary>
    private ObjectSet<T> GetObjectSet<T>() where T:class {
        return _db.CreateObjectSet<T>();
    }

    private T GetByPrimaryKey<T>() where T: class
    {
        .....
    }

    public void Delete<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T: class{

        foreach (T item in All<T>().Where(expression))
        {
            GetObjectSet<T>().DeleteObject(item);
        }
    }

    public void Delete<T>(T item) where T : class {
        GetObjectSet<T>().DeleteObject(item);
    }

    public void DeleteAll<T>() where T : class {
        foreach(T item in All<T>())
        {
            GetObjectSet<T>().DeleteObject(item);
        }
    }

    public void Dispose() {
        _db.Dispose();
    }

    public T Single<T>(System.Linq.Expressions.Expression<Func<T, bool>> expression) where T:class {
        return GetObjectSet<T>().SingleOrDefault(expression);
    }

    public IQueryable<T> All<T>() where T : class {
        return GetObjectSet<T>().AsQueryable();
    }

    public void Add<T>(T item) where T : class {
        GetObjectSet<T>().AddObject(item);
    }
    public void Add<T>(IEnumerable<T> items) where T : class {
        foreach (T item in items)
        {
            GetObjectSet<T>().AddObject(item);
        }
    }
    public void Update<T>(T item) where T : class {
        //nothing needed here
    }
}
}

Ответы [ 4 ]

17 голосов
/ 15 июня 2010

Итак, я наконец-то смог выяснить, как заставить это работать. Хотелось бы, чтобы я не потерял ссылку на блог, который я прочитал вчера вечером, потому что я не написал код.

public T GetByPrimaryKey<T>(int id) where T : class
{
    return (T)_db.GetObjectByKey(new EntityKey(_db.DefaultContainerName + "." + this.GetEntityName<T>(), GetPrimaryKeyInfo<T>().Name, id));
}

string GetEntityName<T>()
{
    string name = typeof(T).Name;
    if (name.ToLower() == "person")
        return "People";
    else if (name.Substring(name.Length - 1, 1).ToLower() == "y")
        return name.Remove(name.Length - 1, 1) + "ies";
    else if (name.Substring(name.Length - 1, 1).ToLower() == "s")
        return name + "es";
    else
        return name + "s";
}

private PropertyInfo GetPrimaryKeyInfo<T>()
{
    PropertyInfo[] properties = typeof(T).GetProperties();
    foreach (PropertyInfo pI in properties)
    {
        System.Object[] attributes = pI.GetCustomAttributes(true);
        foreach (object attribute in attributes)
        {
            if (attribute is EdmScalarPropertyAttribute)
            {
                if ((attribute as EdmScalarPropertyAttribute).EntityKeyProperty == true)
                    return pI;
            }
            else if (attribute is ColumnAttribute)
            {

                if ((attribute as ColumnAttribute).IsPrimaryKey == true)
                    return pI;
            }
        }
    }
    return null;
}

Надеюсь, это поможет кому-то еще. Все, что я могу сказать, это то, что это должно быть немного яснее, как это сделать.

15 голосов
/ 02 июня 2010

В каждом объекте EF4 есть свойство, называемое EntityKey, которое содержит массив EntityKeyValues (массив в случае составного ключа).

Вы можете ссылаться на это непосредственно на экземпляре вашей сущности или создать общий вспомогательный метод, который делает это под прикрытием. Если я смогу протестировать пример кода, я выложу его здесь.

Редактировать : EntityKeyValue представляет собой KeyValuePair<TKey, TValue>, где key - это поле первичного ключа объекта, а value - соответствующее значение.

Например, у меня есть объект с именем Company, первичным ключом которого является поле Symbol.

var firstCompany = (from c in context.Companies select c).FirstOrDefault();
var kvp = firstCompany.EntityKey.EntityKeyValues[0];
// kvp shows {[Symbol, FOO]}

В своей песочнице я заметил, что это свойство было null, когда я создал сущность в коде. Но как только я прочитал объект из базы данных, он был правильно заполнен. Таким образом, похоже, что концепция первичного ключа EF4 вступает в действие только после того, как она попадает в базу данных. Хотя вы можете установить его заранее, если хотите.

3 голосов
/ 04 сентября 2017

Я предполагаю, что многие люди останавливаются на этом посте, просто посмотрев "Платформа сущностей, как найти первичный ключ?" независимо от версии EF (как и я). Поэтому я хотел упомянуть, что с EF 6.1 вы также можете создавать методы расширения для получения первичного ключа. Следующий пример и работает отлично.

PS: Я не уверен на 100%, будет ли это работать с составными и составными ключами, хотя.

using System;
using System.Data.Entity;
using System.Data.Entity.Core.Metadata.Edm;
using System.Data.Entity.Infrastructure;
using System.Linq;

namespace System.Data.Entity
{
    public static class DbContextExtensions
    {
        public static string[] GetKeyNames<TEntity>(this DbContext context)
            where TEntity : class
        {
            return context.GetKeyNames(typeof(TEntity));
        }

        public static string[] GetKeyNames(this DbContext context, Type entityType)
        {
            var metadata = ((IObjectContextAdapter)context).ObjectContext.MetadataWorkspace;

            // Get the mapping between CLR types and metadata OSpace
            var objectItemCollection = ((ObjectItemCollection)metadata.GetItemCollection(DataSpace.OSpace));

            // Get metadata for given CLR type
            var entityMetadata = metadata
                    .GetItems<EntityType>(DataSpace.OSpace)
                    .Single(e => objectItemCollection.GetClrType(e) == entityType);

            return entityMetadata.KeyProperties.Select(p => p.Name).ToArray();
        }
    }
}

Исходный источник

2 голосов
/ 06 августа 2011

это кажется излишне длинным? У меня была такая же потребность, и, используя предложения выше (по SethO и denis_n), я использую:

        //get the primary key field name and location for the table
        var primaryFieldName = entry.EntitySet.ElementType.KeyMembers[0].Name ;
        int primaryFieldLocation = entry.CurrentValues.GetOrdinal(primaryFieldName);
        //gets the value pair for the primary key (returns field name + value)
        var primaryField = entry.EntityKey.EntityKeyValues[primaryFieldLocation];
        String primaryFieldValue = primaryField.Value.ToString();

Надеюсь, это поможет всем, кто заинтересован

...