2010-06-02 24 views
11

Estoy tratando de crear un método genérico utilizando EF4 para encontrar la clave principal de un objeto.Entity Framework 4: ¿Cómo encontrar la clave principal?

ejemplo

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

Para dar más información Estoy trabajando fuera de la Tekpub StarterKit y por debajo es la clase que estoy tratando de poner en marcha

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 
    } 
} 
} 

Respuesta

15

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<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; 
} 

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.

+1

Supongo que fue de: http://www.guerrillasyntax.com/index.php/2010/04/24/linq-to-entity-generic-repository/ –

+0

Eso fue todo. Good find –

+3

Técnicamente no válido, ya que esto arrojará resultados incorrectos si hay una clave primaria compuesta. – Nuzzolilo

15

Hay una propiedad en cada Entidad EF4 llamada EntityKey que contiene una matriz de EntityKeyValues (matriz está allí en caso de 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 bajo las cubiertas. Si puedo probar un código de muestra, lo publicaré aquí.

Edición: El EntityKeyValue es un KeyValuePair<TKey, TValue> donde el key es el campo de clave principal de la entidad y la 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, me di cuenta de que esta propiedad estaba null cuando creé la entidad en el 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.

+0

También puede intentar context.Companies.EntitySet.ElementType.KeyMembers –

+2

¿cómo es que no tengo esa propiedad? –

+4

En caso de que alguien llegue a esta tarde ...EntityKey solo existe en objetos que heredan de EntityObject; los únicos que hacen eso son cosas de modelo primero. –

2

esto parece innecesariamente largo? he tenido la misma necesidad, y el uso de 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(); 

Esperanza esto ayuda a cualquiera que esté interesado

+2

¿Es el nombre de columna de valor de "entrada"? –

+0

@rana, asume que solo una columna es una clave principal –

1

supongo que mucha gente pase por este post 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 se muestra el ejemplo y funciona perfectamente bien.

PD: No estoy 100% seguro, si eso funcionara 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<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(); 
     } 
    } 
} 

Original Source

Cuestiones relacionadas