Entity Framework 4: ¿Cómo encontrar la clave principal?

Intento crear un método genérico utilizando EF4 para encontrar la clave principal de un objeto.

ejemplo

public string GetPrimaryKey() { ... } 

Para dar más información, estoy trabajando fuera del Tekpub StarterKit y debajo está la clase que estoy tratando de poner en funcionamiento

 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(); } ///  /// Gets the table provided by the type T and returns for querying ///  private ObjectSet GetObjectSet() where T:class { return _db.CreateObjectSet(); } private T GetByPrimaryKey() where T: class { ..... } public void Delete(System.Linq.Expressions.Expression<Func> expression) where T: class{ foreach (T item in All().Where(expression)) { GetObjectSet().DeleteObject(item); } } public void Delete(T item) where T : class { GetObjectSet().DeleteObject(item); } public void DeleteAll() where T : class { foreach(T item in All()) { GetObjectSet().DeleteObject(item); } } public void Dispose() { _db.Dispose(); } public T Single(System.Linq.Expressions.Expression<Func> expression) where T:class { return GetObjectSet().SingleOrDefault(expression); } public IQueryable All() where T : class { return GetObjectSet().AsQueryable(); } public void Add(T item) where T : class { GetObjectSet().AddObject(item); } public void Add(IEnumerable items) where T : class { foreach (T item in items) { GetObjectSet().AddObject(item); } } public void Update(T item) where T : class { //nothing needed here } } } 

Así que finalmente pude descubrir cómo hacer que esto funcione. Ojalá no hubiera perdido el enlace al blog que leí anoche porque no escribí el código.

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

Espero que esto ayude a alguien más. Todo lo que puedo decir es que debería ser un poco más claro sobre cómo hacer esto.

Hay una propiedad en cada entidad EF4 llamada EntityKey que contiene una matriz de EntityKeyValues (la matriz está allí en el caso de la clave compuesta).

Puede hacer referencia a esto directamente en la instancia de su entidad o crear un método de ayuda genérico que lo haga debajo de las coberturas. Si puedo probar un código de muestra, lo publicaré aquí.

Editar : EntityKeyValue es un KeyValuePair donde la key es el campo de clave principal de la entidad y el value es el valor asociado.

Por ejemplo, tengo una entidad llamada Company cuya clave principal es el campo Symbol .

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

En mi caja de arena, noté que esta propiedad era null cuando creé la entidad en código. Pero una vez que leí la entidad de la base de datos, estaba correctamente poblada. Por lo tanto, parece que el concepto EF4 de una clave principal solo entra para jugar una vez que llega a la base de datos. Sin embargo, puede establecerlo explícitamente de antemano, si lo desea.

Supongo que mucha gente pasa por esta publicación simplemente mirando “Entity framework cómo encontrar la clave principal”. independientemente de la versión de EF (como yo). Así que quería mencionar que con EF 6.1, también puede crear métodos de extensión para obtener la clave principal. A continuación está el ejemplo y funciona perfectamente bien.

PD: No estoy 100% seguro, si eso funcionaría con las teclas compuestas y compuestas aunque.

 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(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(DataSpace.OSpace) .Single(e => objectItemCollection.GetClrType(e) == entityType); return entityMetadata.KeyProperties.Select(p => p.Name).ToArray(); } } } 

Fuente original

esto parece innecesariamente largo? He tenido la misma necesidad, y utilizando las sugerencias anteriores (por SethO y denis_n), estoy usando:

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

Espero que esto ayude a cualquiera que esté interesado