2010-06-02 15 views
11

Próbuję utworzyć ogólną metodę przy użyciu EF4, aby znaleźć klucz podstawowy obiektu.Entity Framework 4: Jak znaleźć klucz podstawowy?

przykład

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

Aby dać więcej informacji pracuję off z Tekpub StarterKit i poniżej jest klasa Próbuję wstać i uruchomiony

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

Odpowiedz

15

W końcu udało mi się dowiedzieć, jak to zrobić. Szkoda, że ​​nie straciłem linku do bloga, który czytałem wczoraj, ponieważ nie napisałem kodu.

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

Mam nadzieję, że to pomoże komuś innemu. Wszystko, co mogę powiedzieć, to to, że powinno to być nieco jaśniejsze, jak to zrobić.

+1

Zgaduję, że pochodziło z: http://www.guerrillasyntax.com/index.php/2010/04/24/linq-to-entity-generic-repository/ –

+0

To było to. Dobre znalezisko –

+3

Technicznie nieważne, ponieważ spowoduje to zwrócenie nieprawidłowych wyników, jeśli istnieje złożony klucz podstawowy. – Nuzzolilo

15

Jest nieruchomość na każdy Jednostka EF4 o nazwie EntityKey, która zawiera tablicę o numerze EntityKeyValues (tablica istnieje w przypadku klucza złożonego).

Można odwoływać się do tego bezpośrednio na instancji jednostki lub utworzyć ogólną metodę pomocniczą, która wykonuje to pod okładkami. Jeśli uda mi się przetestować jakiś przykładowy kod, opublikuję go tutaj.

Edit: The EntityKeyValue jest KeyValuePair<TKey, TValue> gdzie key jest kluczem Pole podstawowe jednostki i value jest związana wartość.

Np. Mam podmiot o nazwie Company, którego klucz podstawowy to pole Symbol.

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

W mojej piaskownicy, zauważyłem ten obiekt był null kiedy utworzył podmiot w kodzie. Ale kiedy przeczytałem encję z bazy danych, została ona poprawnie wypełniona. Wygląda więc na to, że koncepcja EF4 klucza podstawowego pojawia się tylko wtedy, gdy trafi do bazy danych. Chociaż, jeśli chcesz, możesz go wyraźnie ustawić z wyprzedzeniem.

+0

Można także spróbować context.Companies.EntitySet.ElementType.KeyMembers –

+2

dlaczego nie mam tej nieruchomości? –

+4

Na wypadek, gdyby ktoś się spóźnił ...Obiekt EntityKey istnieje tylko w przypadku obiektów dziedziczących po EntityObject; jedyni, którzy to robią to rzeczy pierwsze. –

2

wydaje się to niepotrzebnie długie? miałem te same potrzeby, a przy użyciu wskazówek powyżej (autorem SethO i denis_n), używam:

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

Nadzieja to pomaga każdemu, kto jest zainteresowany

+2

Czy nazwa kolumny "entry" to wartość? –

+0

@rana zakładasz, że tylko jedna kolumna jest kluczem podstawowym –

1

Zakładam wielu ludzi zatrzymać tego posta po prostu patrząc "Struktura Entity, jak znaleźć klucz podstawowy?" niezależnie od wersji EF (tak jak ja). Tak więc chciałem wspomnieć, że z EF 6.1 można również utworzyć metody rozszerzeń, aby uzyskać klucz podstawowy. Oto przykład i działa idealnie dobrze.

PS: Nie jestem w 100% pewien, czy działałoby to z kluczami złożonymi i złożonymi.

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