Entity Framework 4: How to find the primary key?
I am trying to create a generic method using EF4 to find the primary key of an object.
example
public string GetPrimaryKey<T>()
{
...
}
To give more info I am working 开发者_JS百科off of the Tekpub StarterKit and below is the class I am trying to get up and running
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
}
}
}
So I was finally able to find out how to get this to work. I wish I hadn't lost the link to the blog I read last night as I didn't write the code.
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;
}
I hope this helps someone else. All I can say is that it should be a little clearer on how to do this.
There is a property on each EF4 entity called EntityKey
which contains an array of EntityKeyValues
(array is there in case of compound key).
You could reference this directly on your entity instance or create a generic helper method that does this under the covers. If I can test-drive some sample code, I'll post it up here.
Edit: The EntityKeyValue is a KeyValuePair<TKey, TValue>
where the key
is the primary key field of the entity and the value
is the associated value.
E.g., I have an entity called Company
whose primary key is the field Symbol
.
var firstCompany = (from c in context.Companies select c).FirstOrDefault();
var kvp = firstCompany.EntityKey.EntityKeyValues[0];
// kvp shows {[Symbol, FOO]}
In my sandbox, I noticed this property was null
when I created the entity in code. But once I read the entity from the database, it was correctly populated. So, it appears that the EF4 concept of a primary key only comes in to play once it hits the database. Although, you are free to set it explicitly ahead of time, if you wish.
I assume many people stop by this post just by looking "Entity framework how to find the primary key?" regardless EF version (like me). So I wanted to mentioned that with EF 6.1, you can also create an extension methods to get the primary key. Following is the example and works perfectly fine.
PS: I am not 100% sure, if that would work with composite and compound keys tho.
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
this seems needlessly long? I have had the same need, and using the suggestions above (by SethO and denis_n), i am using:
//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();
Hope this helps anyone who is interested
精彩评论